奇怪的useEffect无限循环

问题描述

通过我们用来处理 apiCalls 的钩子看到一些奇怪的事情,其中​​ api 响应结构的突然变化在我们的应用程序中触发了无限请求循环。我已将其精简到重现错误的基本要求。

import React,{useEffect,useState,useMemo} from 'react';

const mockedApiCall = (resolvedValue,time) =>
    new Promise((resolve,reject) => setTimeout(() => resolve(resolvedValue),time));

const useHook = (query) => {
    const [error,setError] = useState(null);
    const [data,setData] = useState([]);

    useEffect(() => {
        const asyncoperation = async () => {
            const response = await mockedApiCall([],1000);

            // Testing something i kNow will fail:
            const mappedData = response.data.map(a => a);

            // if the above didn't fail we would update data
            setData(mappedData);
        };

        // Clear old errors
        setError(null);
        // trigger operation
        asyncoperation().catch(e => {
            console.log('fail');
            setError('Some error');
        });
    },[query]);

    return [data,error];
}


const HookIssue = props => {
    const [data,error] = useHook({foo: 'bar'}); // Creates infinite loop
    //const [data,error] = useHook('foo') // only fails a single time

    // Alternative solution that also doesn't create infinite loop
    // const query = useMemo(() => ({foo: 'bar'}),[]);
    // const [data,error] = useHook(query);


    return null;
};

export default HookIssue;

有人知道这里发生了什么吗?这个钩子应该只监听查询变量的变化,但在某些情况下会陷入无限循环。

编辑:为了给这个问题添加一些更奇怪的东西,如果我删除 useEffect 中的两个 setError 调用中的一个(或两个),它也会防止无限循环。

解决方法

之所以会出现此循环,是因为您将对象传递给 useHook()。对象是引用类型——也就是说,它们不是由它们的值定义的。由于这个原因,以下语句的计算结果为 false:

{ foo: "bar" } === { foo: "bar" }

每次渲染 HookIssue 时,都会创建一个新的 { foo: "bar" } 对象。这被传递给依赖数组中的 useEffect(),但是因为对象是引用类型 - 每个渲染上的每个对象都有不同的引用 - React 将始终“看到”query 值已更改。

这意味着每次 asyncOperation 完成后,都会导致重新渲染 HookIssue,从而触发另一个效果。

要解决此问题,您需要将值类型传递给依赖项数组,或者您需要通过使用 {{} 来“稳定”query 对象1}}。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...