Home > Back-end >  React - Changing state in context not rerendering DOM
React - Changing state in context not rerendering DOM

Time:10-06

I have useContext which save all loggedUsers in an array. Each user has few properties, one of them is messages array, which stores objects with two properties. I am using this context through custom hook inside of component, like so: const { loggedUsers, setLoggedUsers } = useUsers();. The context looks like this:

export const UsersProvider = ({ children }: Props) => {
  const [loggedUsers, setLoggedUsers] = useState<any[]>([]);
  const socket = useSocket();

  const addUser = (user: any) => {
    console.log(typeof loggedUsers);
    const users: any[] = loggedUsers;
    users.push(user);
    setLoggedUsers(users);
  };

  const sortUsers = (users: any) => {
    users.forEach((user: any) => {
      user.self = user.userID === socket?.id;
    });

    let sorted = users.sort((a: any, b: any) => {
      if (a.self) return -1;
      if (b.self) return 1;
      if (a.username < b.username) return -1;
      return a.username > b.username ? 1 : 0;
    });
    setLoggedUsers(sorted);
  };

  return (
    <UsersContext.Provider
      value={{ loggedUsers, setLoggedUsers, addUser, sortUsers }}
    >
      {children}
    </UsersContext.Provider>
  );
};

User in array looks like this:

{userID: 'Cm6vG0udcV6vl7MEAAAB', userName: 'test123', messages: [{message: 'dsada', fromSelf: true}], self: false, connected: true}

I am updating the context/state like so, but it doesn't force rerender, so my messages between users are not shown. What could be the problem? If I console.log loggedUsers the messages are there, but they do not show up on DOM. If you need more code, let me know.

socket.on('private message', listenerMessage);

const listenerMessage = async (msgInfo: any) => {
    console.log(msgInfo);
    console.log(msgInfo.from);
    const usersList = loggedUsers;
    for (let i = 0; i < usersList.length; i  ) {
      const user = usersList[i];
      if (user.userID === msgInfo.from) {
        user.messages.push({
          message: msgInfo.message,
          fromSelf: false,
        });

        if (user !== state.userID) {
          user.hasNewMessages = true;
        }
        console.log(loggedUsers, 'onprivatemessage');
        setLoggedUsers(usersList);
        break;
      }
    }
  };

const onMessage = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (state.userName) {
      socket?.emit('private-message', {
        message: mess,
        to: state.userID,
      });
      const usersList = loggedUsers;
      for (let i = 0; i < usersList.length; i  ) {
        const user = usersList[i];
        if (user.userID === state.userID) {
          user.messages.push({ message: mess, fromSelf: true });
          console.log(loggedUsers, 'onmessage');
        }
      }
      setLoggedUsers(usersList);
    }

    setMess('');
  };

CodePudding user response:

You shouldn't mutate useState's state directly. Clone it before:

const usersList = [...loggedUsers]
const usersList = loggedUsers.slice()

Here :

setLoggedUsers(usersList);

You are passing the same state array to setLoggedUsers. Cloning the state will solve this.

What you can do is this :

setLoggedUsers(prev=>{
        const prevUserList = prev.slice()
        //your for loop or anything
        return prevUserList;
});

Also here :

  const usersList = loggedUsers;
  for (let i = 0; i < usersList.length; i  ) {
    const user = usersList[i];
    if (user.userID === state.userID) {
      user.messages.push({ message: mess, fromSelf: true });
      console.log(loggedUsers, 'onmessage');
    }
  }

You never update usersList , i don't know if that is wanted.

CodePudding user response:

you using const while you updating the objects. try using let and not const. const objects should not be changed.

  • Related