【问题标题】:How to implement this without triggering an infinite loop with useEffect如何在不使用 useEffect 触发无限循环的情况下实现这一点
【发布时间】:2021-02-25 22:52:30
【问题描述】:

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

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

上述工作正常,因为它始终将第一次发送的用户保存在 a 道具中,并始终给予优先权。现在我的问题是我想将属性添加到状态中的这些用户的列表中,但无论我做什么,我的组件都会不断进入无限循环并使应用程序崩溃。我确实知道发生这种情况的原因,但不知道如何解决。以下是我试图实现的目标,这让我陷入了无限循环。

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 商店中。

有人可以指导我正确的方向吗? 谢谢!

【问题讨论】:

  • 使用 setUsers 的回调语法并从依赖数组中省略 userssetUsers(currentValue => { /* do logic using current value and return new value */ });
  • 感谢您的回复。当我尝试使用 useCallback 时,我传入了 userDataFromApi 但使用该逻辑我仍然必须添加 setUsers 和 and 用户作为依赖项。未定义状态时设置状态的逻辑在哪里,可能是我将 userDataFromApi 和 user 都传递给用 useCallback 包装的函数吗?
  • 用示例代码查看我的答案。

标签: javascript reactjs


【解决方案1】:

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

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 =&gt; 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]);

【讨论】:

  • 我会告诉你这是否有效,我收到一些打字稿错误,即 setUsers 不接受函数,只接受数组。所以我想我必须改变它,让它接受一个返回数组的函数,我相信
  • setUsers 是否来自 useState 钩子?
  • 它是一个将用户存储在 Redux 存储中的调度。公共 setUsers(users?: users[]): void { this.draftState = {...this.draftstate, users} }
  • 你认为如果有办法让它工作而不必将参数类型更改为reducer中的回调,猜测上面的方法当然行不通,因为参数需要是一个数组和不是函数。我必须将主用户列表保留在 redux 中,以便可以从应用程序中的其他几个地方轻松访问它。
  • 是的,以上内容不适用于您的 reducer 函数,我假设您使用的是 useState,查看问题,我可以清楚地看到您提到在 Redux 中设置状态。给我2秒
猜你喜欢
  • 1970-01-01
  • 2020-03-28
  • 1970-01-01
  • 1970-01-01
  • 2014-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多