useEffect React钩子将在每次更改时运行传入函数。可以对其进行优化,使其仅在所需属性更改时调用。

如果我想从componentDidMount调用初始化函数,而不是在更改时再次调用它,该怎么办?假设我想加载一个实体,但加载函数不需要来自组件的任何数据。我们如何使用useEffect钩子来实现这个?

class MyComponent extends React.PureComponent {
    componentDidMount() {
        loadDataOnlyOnce();
    }
    render() { ... }
}

如果使用钩子,可能是这样的:

function MyComponent() {
    useEffect(() => {
        loadDataOnlyOnce(); // this will fire on every change :(
    }, [...???]);
    return (...);
}

当前回答

function useOnceCall(cb, condition = true) {
  const isCalledRef = React.useRef(false);

  React.useEffect(() => {
    if (condition && !isCalledRef.current) {
      isCalledRef.current = true;
      cb();
    }
  }, [cb, condition]);
}

并利用它。

useOnceCall(() => {
  console.log('called');
})

or

useOnceCall(()=>{
  console.log('Fetched Data');
}, isFetched);

其他回答

我们必须停止思考组件生命周期方法(即componentDidMount)。我们必须开始考虑效果。React效果不同于老式的类-生命周期方法。

默认情况下,效果在每个渲染周期后运行,但也有选择退出这种行为的选项。若要选择退出,可以定义依赖项,这意味着仅在对其中一个依赖项进行更改时才执行效果。

如果显式地定义一个效果没有依赖关系,则该效果只在第一个渲染循环之后运行一次。

第一个解决方案(带有ESLint-complaint)

所以,你的例子的第一个解决方案是:

function MyComponent() {

    const loadDataOnlyOnce = () => {
      console.log("loadDataOnlyOnce");
    };

    useEffect(() => {
        loadDataOnlyOnce(); // this will fire only on first render
    }, []);
    return (...);
}

但是React Hooks ESLint插件会抱怨这样的事情:

React钩子useEffect缺少依赖项:loadDataOnlyOnce。要么包含它,要么删除依赖数组。

起初,这个警告似乎很烦人,但请不要忽视它。它可以帮助你更好地编码,并将你从“陈旧的闭包”中拯救出来。如果你不知道什么是“过期闭包”,请阅读这篇很棒的文章。

第二种解决方案(正确的方法,如果依赖关系不依赖于组件)

如果我们将loadDataOnlyOnce添加到依赖数组中,我们的效果将在每次呈现循环后运行,因为loadDataOnlyOnce的引用在每次呈现时都会改变,因为函数被销毁(垃圾回收)并创建一个新函数,但这正是我们不想要的。

我们必须在渲染周期中保持loadDataOnlyOnce的相同引用。

所以只要把函数定义移到上面:

const loadDataOnlyOnce = () => {
  console.log("loadDataOnlyOnce");
};

function MyComponent() {
    useEffect(() => {
        loadDataOnlyOnce(); // this will fire only on first render
    }, [loadDataOnlyOnce]);
    return (...);
}

通过这个更改,您可以确保loadDataOnlyOnce的引用永远不会改变。因此,您还可以安全地将引用添加到依赖项数组中。

第三种解决方案(正确的方法,如果依赖依赖于组件)

如果效果的依赖关系(loadDataOnlyOnce)依赖于组件(需要道具或状态),React内置的useCallback-Hook。

useCallback-Hook的基本含义是在呈现循环期间保持函数的引用相同。

function MyComponent() {
    const [state, setState] = useState("state");

    const loadDataOnlyOnce = useCallback(() => {
      console.log(`I need ${state}!!`);
    }, [state]);

    useEffect(() => {
        loadDataOnlyOnce(); // this will fire only when loadDataOnlyOnce-reference changes
    }, [loadDataOnlyOnce]);
    return (...);
}

将依赖项数组保留为空。希望这能帮助你更好地理解。

   useEffect(() => {
      doSomething()
    }, []) 

空依赖数组只在挂载时运行一次

useEffect(() => {
  doSomething(value)
}, [value])  

将值作为依赖项传递。如果依赖关系自上次以来发生了变化,该效果将再次运行。

useEffect(() => {
  doSomething(value)
})  

没有依赖。这个在每次渲染后都会被调用。

我在网上花了一段时间后发现的。useEffect在组件挂载时触发一次,然后组件卸载并再次挂载,useEffect再次触发。你得多看看React文档,他们为什么这么做。

我使用了自定义钩子。卸载时,你必须改变你的useRef状态。在这种情况下,不要忘记返回语句:当组件卸载时,useEffect在返回后运行清理函数。

import React, { useEffect, useRef } from "react"

const useDidMountEffect = (
  func: () => void,
  deps: React.DependencyList | undefined
) => {
  const didMount = useRef(false)

  useEffect(() => {
    if (didMount.current) {
      func()
    }
    return () => {
      didMount.current = true
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, deps)
}

export default useDidMountEffect

使用它像正常的useEffect:

  useDidMountEffect(() => {
    // your action    
  }, [])

博士TL;

useEffect(yourCallback,[]) -只在第一次渲染后触发回调。

详细解释

默认情况下,useEffect在每次呈现组件后运行(因此会产生效果)。

当在你的组件中放置useEffect时,你告诉React你想要运行回调作为一个效果。React将在渲染和执行DOM更新后运行效果。

如果你只传递一个回调-回调将在每次渲染后运行。

如果传递第二个参数(数组),React将在第一次渲染后和每次数组中的一个元素被更改时运行回调。例如,当放置useEffect(() => console.log('hello'), [someVar, someOtherVar])时,回调将在第一次渲染之后和someVar或someOtherVar中的一个被更改的任何渲染之后运行。

通过将第二个参数传递给一个空数组,React将在每次渲染数组后进行比较,并且不会看到任何更改,因此只在第一次渲染后调用回调。

如果你只是在渲染后调用useeffect中的函数,你添加一个空数组作为useeffect的第二个参数

useEffect=(()=>{
   functionName(firstName,lastName);
},[firstName,lastName])