如何仅使用一次React useEffect调用加载函数

时间:2018-11-02 14:58:56

标签: javascript reactjs react-hooks

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

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

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

带有钩子,可能看起来像这样:

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

9 个答案:

答案 0 :(得分:107)

如果只想在初始渲染后运行赋予useEffect的函数,则可以给它一个空数组作为第二个参数。

function MyComponent() {
  useEffect(() => {
    loadDataOnlyOnce();
  }, []);

  return <div> {/* ... */} </div>;
}

答案 1 :(得分:10)

将空数组作为第二个参数传递给useEffect。这有效地告诉了React,并引用了文档:

  

这告诉React您的效果不依赖于道具或状态的任何值,因此它不需要重新运行。

下面是一个片段,您可以运行它以显示它的工作原理:

function App() {
  const [user, setUser] = React.useState(null);

  React.useEffect(() => {
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        setUser(data.results[0]);
      });
  }, []); // Pass empty array to only run once on mount.
  
  return <div>
    {user ? user.name.first : 'Loading...'}
  </div>;
}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

答案 2 :(得分:8)

TL; DR

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

详细说明

useEffect默认在每次渲染组件后运行(从而产生效果)。

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

如果仅传递回调-回调将在每次渲染后运行。

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

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

答案 3 :(得分:2)

我喜欢定义一个mount函数,它以与useMount相同的方式欺骗​​EsLint,我发现它更不言自明。

const mount = () => {
  console.log('mounted')
  // ...

  const unmount = () => {
    console.log('unmounted')
    // ...
  }
  return unmount
}
useEffect(mount, [])

答案 4 :(得分:1)

自定义useMountEffect挂钩

仅在组件挂载后才运行函数是一种常见的模式,它证明了自己的钩子隐藏了实现细节。

const useMountEffect = (fun) => useEffect(fun, [])

然后在代码中的任何地方使用它。

function MyComponent() {
    useMountEffect(() => {
        loadDataOnlyOnce()
    });
    return (...);
}

关于useMountEffect挂钩

在将useEffect与第二个数组参数一起使用时,React将在挂载(初始渲染)后以及数组中的一个或多个值更改后运行回调。由于我们传递了一个空数组,因此它将仅在安装后运行。

useMountEffect实现了React挂钩的构想-基于本机/原始挂钩组成自定义挂钩。

答案 5 :(得分:0)

我们在GitHub上开发了一个模块,该模块具有用于获取数据的钩子,因此您可以按以下目的使用它:

import { useFetching } from "react-concurrent";

const app = () => {
  const { data, isLoading, error , refetch } = useFetching(() =>
    fetch("http://example.com"),
  );
};

您可以提出要求,但欢迎任何PR。 https://github.com/hosseinmd/react-concurrent#react-concurrent

答案 6 :(得分:0)

将依赖项数组留为空白。希望这可以帮助您更好地理解。

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

空依赖项数组只能在Mount上运行一次

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

通过value作为依赖项。如果自上次以来依存关系发生了变化,效果将再次运行。

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

无依赖性。每次渲染后都会调用它。

答案 7 :(得分:0)

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');
})

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

答案 8 :(得分:0)

这是我对 Yasin 的回答的版本。

import {useEffect, useRef} from 'react';

const useOnceEffect = (effect: () => void) => {
  const initialRef = useRef(true);

  useEffect(() => {
    if (!initialRef.current) {
      return;
    }
    initialRef.current = false;
    effect();
  }, [effect]);
};

export default useOnceEffect;

用法:

useOnceEffect(
  useCallback(() => {
    nonHookFunc(deps1, deps2);
  }, [deps1, deps2])
);