为什么React钩子useEffect无限运行?

时间:2018-12-30 09:23:04

标签: reactjs

我用create-react-app创建了一个项目,
我正在尝试React挂钩,
在下面的示例中,
句子console.log(articles)无休止地运行:

import React, {useState, useEffect} from "react"
import {InfiniteScroller} from "react-iscroller";
import axios from "axios";
import './index.less';

function ArticleList() {
    const [articles, setArticles] = useState([]);

    useEffect(() => {
        getArticleList().then(res => {
           setArticles(res.data.article_list);
            console.log(articles);  
        });
    },[articles]);

    const getArticleList = params => {
        return axios.get('/api/articles', params).then(res => {
            return res.data
        }, err => {
            return Promise.reject(err);
        }).catch((error) => {
            return Promise.reject(error);
        });
    };


    let renderCell = (item, index) => {
        return (
            <li key={index} style={{listStyle: "none"}}>
                <div>
                    <span style={{color: "red"}}>{index}</span>
                    {item.content}
                </div>
                {item.image ? <img src={item.image}/> : null}
            </li>
        );
    };

    let onEnd = () => {
        //...
    };


    return (
        <InfiniteScroller
            itemAverageHeight={66}
            containerHeight={window.innerHeight}
            items={articles}
            itemKey="id"
            onRenderCell={renderCell}
            onEnd={onEnd}
        />
    );
}

export default ArticleList;

为什么?如何处理?

https://i.stack.imgur.com/SHLjh.gif

4 个答案:

答案 0 :(得分:4)

React useEffect将第二个参数与其先前的值进行比较,这取决于您的情况。但是在javascript中比较对象的结果始终为假,请尝试在浏览器控制台中比较[] === [],您将得到假。因此,解决方案是不比较整个对象,而是比较articles.lenght

const [articles, setArticles] = useState([]);

useEffect(() => {
    getArticleList().then(res => {
       setArticles(res.data.article_list);
        console.log(articles);  
    });
},[articles.length]);

答案 1 :(得分:2)

这仅仅是因为您无法使用===在JavaScript中比较两个数组(简单来说)对象。在这里,您基本上要做的是将articleName的先前值与articleName的当前值进行比较,该值将始终返回false。自

  

通过引用进行的比较基本上检查了给定的对象是否指向内存中的相同位置。

此处不是,因此每次进行重新渲染。 解决方案是传递一个像数组长度这样的常量,或者如果您想进行严格检查,然后从数组中的所有元素创建一个哈希,然后在每个渲染器上进行比较。

答案 2 :(得分:1)

以上答案在正确的轨道上,但让我补充一点。这是React的useEffect钩子的陷阱。

类似于Redux减速器的工作方式。每次将对象作为useEffect数组内的第二个参数传递时,都将其视为内存中的另一个对象。

例如,如果您致电

useEffect(() > {}, [{articleName: 'Hello World'}])

,然后进行重新渲染,每次都将再次调用它。因此,传递文章的长度是一种绕过此问题的方法。如果您不希望再次调用该函数,则可以传入一个空数组作为参数。

答案 3 :(得分:0)

我遇到了一个数组对象。我发现仅传递[array.length]作为第二个参数不起作用,所以我不得不创建第二个状态来跟上array.length的变化,并在处理数组时调用setArrayLength。

const = [array, setArray] = useState([]);
const = [arrayLength, setArrayLength] = useState(array.length)

useEffect(() => {
        const fetchData = async () => {
            const result = await axios.get(// whatever you need);
            setArray(result.data)
        };
        fetchData();

    }, [arrayLength]);

const handleArray () => {
   // Do something to array
   setArrayLength(array.length);
}

利用useRef进行了深入的比较,您可以看到代码here。蛋头上有一个随附的视频,但它在付费专区后面。