如何在不触发使用效果的无限循环的情况下实现这一点



我有一个显示用户列表的组件。组件第一次加载时,它给出一个包含一些数据的所有用户列表。在此之后,基于与组件的一些交互,我得到了带有一些额外属性的更新的用户列表。问题是,所有后续响应只返回具有这些额外属性的用户。因此,我需要的是保存用户的初始状态,其中包含所有用户的列表,并且在随后的任何更改中不断更新/添加此状态,而不必用新状态替换整个状态,因为我不想丢失用户列表。到目前为止,我所做的是在Redux中设置第一次渲染的状态,条件是:

useEffect(() => {
if(users === undefined) {
setUsers(userDataFromApi)
}
userList = users || usersFromProp
})

上面的工作很好,因为它总是保存用户发送的第一次在一个道具,并始终优先考虑它。现在我的问题是,我想要添加属性到那些状态的用户列表,但不管我做什么,我的组件一直进入一个无限循环,崩溃的应用程序。我知道这发生的原因,但不知道如何解决它。下面是我试图实现的东西,它把我扔进了一个无限循环。

useEffect(() => {
if(users === undefined) {
setUsers(userDataFromApi)
} else {
//Users already exist in state
const mergedUserData = userDataFromApi.map(existingUser => {
const matchedUser = userDataFromApi.find(user => user.name === existingUser.name);
if (matchedUser) {
existingUser.stats = user.stats;
}
return existingUser;

})
setUsers(mergedUserData)
}
}, [users, setUsers, userDataFromApi])
到目前为止,我已经尝试将代码包装在else块中的一个单独的函数中,然后从useEffect中调用它。我还试图将所有逻辑提取到一个单独的函数中,并使用useCallback包装,但仍然没有成功。正因为我要加上这些依赖项,它就会一直进入一个无限循环。需要提到的一件重要的事情是,我不能跳过任何useCallback或useEffect的依赖,因为linter会显示警告。我需要保持日志干净。setUsers也是一个分派道具。我需要在Redux存储中保留主用户列表。 谁能给我指个正确的方向?谢谢你!

既然这是基于交互的,那么这就不能由交互引起的事件来处理吗?

const reducer = (state, action) => {
switch (action.type) {
case "setUsers":
return {
users: action.payload
};
default:
return state;
}
};
const Example = () => {
const dispatch = useDispatch();
const users = useSelector(state => state.users)
useEffect(() => {
const asyncFunc = async () => {
const apiUsers = await getUsersFromApi();
dispatch({ type: "setUsers", payload: apiUsers });
};
// Load user data from the api and store in Redux.
// Only do this on component load.
asyncFunc();
}, [dispatch]);

const onClick = async () => {
// On interaction (this case a click) get updated users.
const userDataToMerge = await getUpdatedUserData();
// merge users and assign to the store.
if (!users) {
dispatch({ type: "setUsers", payload: userDataToMerge  });
return;
}

const mergedUserData = users.map(existingUser => {
const matchedUser = action.payload.find(user => user.name === existingUser.name);
if (matchedUser) {
existingUser.stats = user.stats;
}
return existingUser;
});
dispatch({ type: "setUsers", payload: mergedUserData  });
}
return (
<div onClick={onClick}>
This is a placeholder
</div>
);
}

旧答案(useState)

setUsers也可以接受一个回调函数,该函数提供当前状态值作为其第一个参数:setUsers(currentValue => newValue);

你应该能够使用这个来避免把用户放在你的useEffect的依赖数组中。

的例子:

useEffect(() => {
setUsers(currentUsers => {
if(currentUsers === undefined) {
return userDataFromApi;
} else {
//Users already exist in state
const mergedUserData = currentUsers.map(existingUser => {
const matchedUser = userDataFromApi.find(user => user.name === existingUser.name);
if (matchedUser) {
existingUser.stats = user.stats;
}
return existingUser;
});
return mergedUserData;
}
});
}, [setUsers, userDataFromApi]);

最新更新