useEffect调用了两次

时间:2020-04-06 21:26:40

标签: javascript reactjs react-hooks

我知道为什么会发生这种情况,但是想知道什么是避免这种情况的正确方法。 useEffect被调用两次。一个数据= null,然后是“真实”数据。

const [data, setData] = useState(null);
someServiceToFetchData.then(freshData => setData(freshData))

useEffect(() => {
    console.log("useEffect called");
}, [data]);

我应该到处做这样的事情吗?

useEffect(() => {
    if (data) {
        console.log("useEffect called with real data");
    }
}, [data]);

2 个答案:

答案 0 :(得分:3)

众所周知,useEffect在初始渲染时以及在依赖数组的值的后续更改中都会被调用一次。

在您的情况下,要跳过useEffect的初始执行(空情况),请编写一个小自定义钩子,例如可以在需要时通用的钩子。

组件

import React,  { useState, useEffect } from 'react';
import useEffectSkipInitialRender from "./hook";

const Test = (props) => {
    const [data, setData] = useState(null);

    new Promise((res, rej) => res('my data')).then(freshData => setData(freshData));

    useEffectSkipInitialRender(() => {
        console.log("useEffect called");
    }, [data]);

    return <div>hi</div>
};

export default Test;

自定义钩子

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

const useEffectSkipInitialRender = (callback, dataArr) => {
    const [data, setData] = useState(null);
    const isInitialRender = useRef(true);// in react, when refs are changed component dont re-render 

    useEffect(() => {
        if(isInitialRender.current){// skip initial execution of useEffect
            isInitialRender.current = false;// set it to false so subsequent changes of dependency arr will make useEffect to execute
            return;
        }
        return callback();
    }, dataArr);

};

export default useEffectSkipInitialRender;

如果您希望效果逻辑仅在数据不为null且数据发生更改时才运行,则可以这样编写自定义钩子:

import React,  { useEffect } from 'react';

const useEffectOnDataChange = (callback, dataArr) => {
    const someIsNull = dataArr.some(data => data == null);

    useEffect(() => {
        if (someIsNull) return;
        return callback();
    }, dataArr);

}

答案 1 :(得分:0)

如果要跳过第一个渲染,可以使用这样的状态标志。我发现下面的方法不太复杂。

import React, { useState } from 'react';

const Comp = ({dep}) => {
    const [rendered, setRendered] = useState(false);

    useEffect(() => {
        if(rendered){
            // do stuff
        }
        
        if( ! rendered ) {
            setRendered(true);
        }
    }, [dep]);

    // rest of the component code

    return <></>
}