【问题标题】:React infinite loop on change state in useEffect()在 useEffect() 中对更改状态做出反应无限循环
【发布时间】:2020-09-18 22:20:50
【问题描述】:

我正在尝试更新 useEffect 中的状态,我得到:

React Hook useEffect 缺少依赖项:'user'。要么包含它,要么删除依赖数组。如果您在“setUser”调用 react-hooks/exhaustive-deps 中只需要“user”,也可以进行功能更新“setUser(u => ...)”

如果我将用户状态作为依赖项,那么我会得到一个无限循环。

    const [user, setUser] = useState({
        displayName: '',
        hasPassword: false,
        ...
    });

    let { userId } = useParams();
    
    useEffect(() => {
    
        const checkPassword = async (userId) => {

            try {
                const res = await UserService.checkPassword(userId);

                if (res.status === 200) {
                    setUser({...user, hasPassword: res.data.hasPassword });
                }

            } catch (err) {
                setUser({...user, hasPassword: false });
            }
        };

        checkPassword(userId);
        
    }, [userId, user]);

如果我从依赖项数组中删除用户,则一切正常,但我正试图摆脱该警告。我只需要执行一次这个函数。

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    useState 在第二个位置返回的方法也接受回调,因此您的代码可以重写为:

    const [user, setUser] = useState({
            displayName: '',
            hasPassword: false,
            ...
        });
    
        let { userId } = useParams();
        
        useEffect(() => {
        
            const checkPassword = async (userId) => {
    
                try {
                    const res = await UserService.checkPassword(userId);
    
                    if (res.status === 200) {
                        setUser(user=>({...user, hasPassword: res.data.hasPassword }));
                    }
    
                } catch (err) {
                    setUser(user=>({...user, hasPassword: false }));
                }
            };
    
            checkPassword(userId);
            
        }, [userId]);
    

    【讨论】:

    • 不知道我可以在 setState 上使用回调
    • 我个人尝试大部分时间都这样使用
    【解决方案2】:

    我猜你可以使用user 之前的状态来代替user 的状态:

    setUser(prevState => ({
       ...prevState,
       hasPassword: res.data.hasPassword
    }));
    

    因此您不需要在依赖数组中使用user。

    最终代码将与建议:

    useEffect(() => {
        const checkPassword = async (userId) => {
           try {
              const res = await UserService.checkPassword(userId);
    
              if (res.status === 200) {
                 setUser(prevState => ({
                    ...prevState,
                    hasPassword: res.data.hasPassword
                 }));
              }
           } catch (err) {
              setUser(prevState => ({
                 ...prevState,
                 hasPassword: false
              }));
           }
        };
    
        checkPassword(userId);    
    }, [userId]);
    

    【讨论】:

      【解决方案3】:

      您可以使用 setState 回调语法:

      setUser(user => ({ ...user, hasPassword: whatever }))
      

      【讨论】:

        【解决方案4】:

        因为你有循环依赖,即

        • user 对象更改时运行效果
        • 实际上,您再次更新了 user 对象

        请注意,您的 user 对象的引用在每次 setUser 调用时都会发生变化,这就是存在无限循环的原因。

        我不建议在您的依赖数组中包含 user,因为如果 userId 发生更改,您可以更新 user 详细信息。

        如果您仍然无法删除 user 依赖关系,您可以使用 JSON.stringify(user) 作为依赖关系,因为该效果仅在您的 user 对象的字符串化表示发生更改时才会运行。

        
         useEffect(() => {
            // your effect code
         }, [userId, JSON.stringify(user)]);
        

        注意:仅当您没有 undefined/NaN 等值时才使用stringify(),因为转换后键会丢失。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-07-26
          • 2023-03-22
          • 1970-01-01
          • 1970-01-01
          • 2020-01-09
          • 2022-08-18
          • 1970-01-01
          • 2021-04-10
          相关资源
          最近更新 更多