使用两个useState值和效果刷新来反应效果依赖性

时间:2020-07-01 22:22:38

标签: reactjs react-hooks use-effect

以下是我的代码

    const [accountUsers, setAccountUsers] = useState([]);
    const [listLoading, setListLoading] = useState(true);
    const [totalAccountUsers, setTotalAccountUsers] = useState(0);

    const getAccountUserList = useCallback(
        async (page = 0, pageSize = 10) => {
            console.log('Kitne users hain??==', accountUsers.length);
            if (
                // TODO: Move this logic to action for reusability
                accountUsers.length < (page + 1) * pageSize &&
                (accountUsers.length < totalAccountUsers || accountUsers.length === 0)
            ) {
                console.log('inside if condition');

                console.log(' Total Account users=', totalAccountUsers);
                console.log(
                    'first condiont=',
                    ' accountUsers.length < (page + 1) * pageSize ',
                    accountUsers.length < (page + 1) * pageSize,
                    ' AND'
                );
                console.log(
                    'second one condition',
                    ' accountUsers.length < totalAccountUsers =',
                    accountUsers.length < totalAccountUsers,
                    ' OR ',
                    'second two condition',
                    '  accountUsers.length === 0',
                    accountUsers.length === 0
                );

                setListLoading(true);
                const data = await getAccountUsers(id, page + 1, pageSize);
                setAccountUsers((users) => users.concat(data.data.data.results));
                setTotalAccountUsers(data.data.data.total);
                setListLoading(false);
            }
        },
        [accountUsers, totalAccountUsers, id]
    );

    const refetchUsers = useCallback(() => {
        setAccountUsers([]);
        setTotalAccountUsers(0);
    }, []);

    useEffect(() => {
        getAccountUserList();
    }, [getAccountUserList]);


    return (
      <>
         <Button onClick={() => refetchUsers()}>Refetch</Button>
         <Button onClick={async ()=> await delete(id); refetchUsers()> Delete </Button> 
      </>
    )

我的refetch函数单独调用时效果很好。但是,当涉及到异步函数时,回调将被调用两次。我感觉在refetch函数中,有两个setStates使它在效果的帮助下两次调用了回调。

为什么会发生这种情况?解决方法是什么? 如果在任何异步操作之前调用refref,它就可以正常工作。在触发效果之前对状态更新进行批处理是否有问题。

1 个答案:

答案 0 :(得分:1)

通过压缩依赖关系树(并忽略一些无关的内容),您的代码可以有效地实现以下效果:

const Home = () => {
  const [accountUsers, setAccountUsers] = useState<number[]>([]);
  const [totalAccountUsers, setTotalAccountUsers] = useState(0);

  React.useEffect(() => {
    const getAccountUserList = async () => {
      const data = await Promise.resolve({results: [1, 2, 3]});
      setAccountUsers((users) => users.concat(data.results));
      setTotalAccountUsers(data.results.length);
    };

    getAccountUserList();
  },
  [accountUsers, totalAccountUsers]);

  const refetchUsers = () => {
    setAccountUsers([]);
    setTotalAccountUsers(0);
  };

  return (
    <>
       <button onClick={() => refetchUsers()}>Refetch</button>
    </>
  );
};

我们可以在这里轻松发现问题-由useEffect挂钩引起的无限重渲染。您看到api被调用的可能性是两次,因为(幸运的是)它已经完成了所有数据的读取,并且代码中的if条件停止了进一步的状态更新。

一些个人建议:

  1. 从一开始就避免useCallback。这是一种优化,我们应该从正确的核心功能开始而不是优化。
  2. 对于用户事件,请尝试避免使用useEffect触发副作用-它可能需要额外的状态更新,并导致不必要的重新渲染。直接在事件处理程序中执行该操作。
  3. 使用不依赖(一次)的useEffect进行初始数据加载。
  4. 尝试避免依赖关系“层次结构”-例如,在您的原始情况下,useEffect依赖于getAccountUserList,后者依赖于其他状态对象。这种层次结构使得读取代码更加困难,并可能导致难以发现的错误。而是尝试构建代码,使它们直接依赖状态。

以下代码(再次简化)适用于您的情况:

const Home = () => {
  const [accountUsers, setAccountUsers] = useState<number[]>([]);
  const [totalAccountUsers, setTotalAccountUsers] = useState(0);

  const getAccountUserList = async () => {
    const data = await Promise.resolve({results: [1, 2, 3]});
    setAccountUsers((users) => users.concat(data.results));
    setTotalAccountUsers(data.results.length);
  };

  React.useEffect(() => {
    getAccountUserList();
  }, []);

  const refetchUsers = () => {
    setAccountUsers([]);
    setTotalAccountUsers(0);
    getAccountUserList();
  };

  return (
    <>
       <button onClick={() => refetchUsers()}>Refetch</button>
    </>
  );
};