【问题标题】:React - Changing state in context not rerendering DOMReact - 在上下文中更改状态而不重新渲染 DOM
【发布时间】:2021-10-05 19:35:42
【问题描述】:

我有 useContext 将所有已登录的用户保存在一个数组中。每个用户都有几个属性,其中之一是消息数组,它存储具有两个属性的对象。我通过组件内部的自定义钩子使用此上下文,如下所示:const { loggedUsers, setLoggedUsers } = useUsers();。上下文如下所示:

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>
  );
};

数组中的用户如下所示:

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

我正在像这样更新上下文/状态,但它不会强制重新呈现,因此我的用户之间的消息不会显示。可能是什么问题呢?如果我console.logloggedUsers 消息就在那里,但它们不会出现在 DOM 上。如果您需要更多代码,请告诉我。

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('');
  };

【问题讨论】:

  • 试试 setLoggedUsers([...usersList]);

标签: javascript reactjs typescript socket.io use-context


【解决方案1】:

您不应该直接改变 useState 的状态。 之前克隆它:

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

这里:

setLoggedUsers(usersList);

您将相同的状态数组传递给 setLoggedUsers。克隆状态将解决这个问题。

你可以这样做:

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

也在这里:

  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');
    }
  }

您从不更新 usersList ,我不知道是否需要。

【讨论】:

  • 因此,如果我理解正确,我必须先使用扩展运算符或splice()... 复制数组,然后使用setLoggedUsers(usersList) 设置此数组副本
  • @Emilija 是的,首先克隆它然后修改它然后设置它。
【解决方案2】:

您在更新对象时使用 const。 尝试使用 let 而不是 const。 不应更改 const 对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-29
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 2017-03-26
    • 2020-10-03
    • 1970-01-01
    相关资源
    最近更新 更多