0

I've tried to change this so many ways, no matter how it's done/explained on other Stack Overflow posts. I have an array of objects, and I am trying to map/loop through the array move one of the objects to the front of the array.

Here's what I've got:

const [users, setUsers] = useState<User[]>([]);
const [primaryUser, setPrimaryUser] = useState<User>({
  id: 0;
  first_name: "",
  last_name: ""
})

useEffect(() => {
  users.map((user, i) => {
    if(user.id === 12345) {
      users.splice(i, 1);
      users.unshift(user)

      setPrimaryUser(user);
    }
  });

  setUsers(users);
}, [])

Whether I map as shown above or use a for loop, I always end up getting the following error: TypeError: Cannot assign to read only property 'x' of object '[object Array]'

Any suggestions/help is greatly appreciated. Thanks in advance!

thelos999
  • 641
  • 2
  • 7
  • 19
  • `if(user.id = 12345)` [this is assignment, not equality](https://stackoverflow.com/questions/11871616/in-javascript-vs/). Moreover, you should probably avoid mutating `users` via `.splice()` and `.unshift()`. Also, as a side note, please don't use `.map` as a simple iteration. If you're not doing a mapping operation, use `.forEach` or a simple loop. – VLAZ Nov 12 '20 at 21:44
  • Thanks, I copied it over incorrectly. I have it as `if(user.id === 12345) {`. Also, I've tried a `forEach` and `for` loop. Still getting the same error every time. – thelos999 Nov 12 '20 at 21:51
  • Then the issue is with mutating the array. You should never mutate state directly with React and this is exactly the problem you get here. – VLAZ Nov 12 '20 at 21:53
  • I've even tried making a copy of the state - `let tempUsers = users`, mutating that, setting it - `setUsers(tempUsers)` and still the same thing. – thelos999 Nov 12 '20 at 21:54
  • 1
    `let tempUsers = users` will ***not*** make a copy of the array! [Copy array by value](https://stackoverflow.com/q/7486085) – VLAZ Nov 12 '20 at 21:55
  • not sure if this is related or just a typo in copying your example, but you've got a semicolon after `id: 0;` in your primaryUser userState call – andersryanc Nov 12 '20 at 21:58

2 Answers2

1

I see the code is used in React, so it is best to avoid modifying the users array in place (for example with .unshift() or .splice()) as React will not see the difference and will not re-render.

It is common practice in React to create new value for the previous one.

I think you can achieve the goal by filtering the users array twice (filter method creates a copy without affecting the original):

  • users with id 12345 - expecting just one and and store it in primaryUser variable
  • users with id not 12345

and combining those arrays:

useEffect(() => {
  const primaryUserID = 12345;
  const [ primaryUser ] = users.filter(user => user.id === primaryUserID)
  setPrimaryUser(primaryUser);

  // Construct array from 'primaryUser' and the rest of users
  const newUsers = [
     primaryUser,
     ...users.filter(user => user.id !== primaryUserID),
  ];
  setUsers(newUsers);
}, [])
Vitalii
  • 2,071
  • 1
  • 4
  • 5
  • 1
    Here's great article about JS array methods, it explains filter/map/reduce using good examples https://www.telerik.com/blogs/functional-programming-with-javascript-object-arrays – Vitalii Nov 12 '20 at 21:57
0

Thanks to both VLAZ and Vitalii (great article), I was able to get it working. Here was my solution:

useEffect(() => {
  let tempUsers = [...users];

  tempUsers.map((user, i) => {
    if(user.id === 12345) {
      tempUsers.splice(i, 1);
      tempUsers.unshift(user)

      setPrimaryUser(user);
    }
  });

  setUsers(tempUsers);
}, [])

THANK YOU!

thelos999
  • 641
  • 2
  • 7
  • 19