React - 在上下文中更改状态而不重新渲染 DOM
React - Changing state in context not rerendering DOM
我有 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}
我正在像这样更新 context/state,但它不会强制重新呈现,因此不会显示用户之间的消息。可能是什么问题呢?如果我 console.log
loggedUsers 消息在那里,但它们不会显示在 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('');
};
您在更新对象时使用了 const。
尝试使用 let 而不是 const。
const 对象不应更改。
你不应该直接改变 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 ,我不知道这是否需要。
我有 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}
我正在像这样更新 context/state,但它不会强制重新呈现,因此不会显示用户之间的消息。可能是什么问题呢?如果我 console.log
loggedUsers 消息在那里,但它们不会显示在 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('');
};
您在更新对象时使用了 const。 尝试使用 let 而不是 const。 const 对象不应更改。
你不应该直接改变 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 ,我不知道这是否需要。