【译】一文看懂 React useEffect hook 的用法

11,415 阅读4分钟

来源: Joseph Mawa. What is useEffect hook and how do you use it?.2020年6月30日

目次

  • 介绍
  • useEffect hook 传递了哪些参数?
  • useEffect 的第一个参数
  • effect 的返回值
  • useEffect 的第二个参数
  • 传递一个函数作为依赖关系
  • 参考文献

这是我关于 react hook 系列的第三篇文章。如果你还没有看前两篇,请按照下面的链接来看。

  1. What is useState hook and how do you use it?
  2. What is useReducer hook and how do you use it?

useEffect hook 简介

钩子是一个函数,它可以让你在不编写ES6类的情况下使用状态和其他react特性。 useEffect钩子是react钩子API的一部分。如果你熟悉react生命周期,useEffect钩子相当于生命周期方法componentDidMountcomponentDidUpdatecomponentWillUnmount的组合。其实根据React文档中关于钩子的介绍useEffect钩子是为了解决ES6类组件的生命周期方法带来的一些挑战而开发的。由于本篇文章介绍的是什么是 effect hook ,以及它的使用方法,所以我就不说它的开发原因了。你可以在这里看一下。 在React功能组件中,我们在 useEffect 钩子里面执行有“副作用”的操作,比如从API中获取数据或者手动更新DOM。

useEffect hook 传递了哪些参数?

useEffect是一个接受两个参数的函数。传递给useEffect的第一个参数是一个名为effect的函数(你可以猜到为什么这个钩子叫useEffect),第二个参数(是可选的)是一个存储依赖关系的数组。下面是它的使用说明。

import React, { useEffect } from "react";
import { render } from "react-dom";
const App = props => {
  useEffect(() => {
    console.log("Effect has been called");
  }); //Second argument to useEffect has been omitted
  return <h1> Hello world! </h1>;
};  
const root = document.getElementById("root");
render(<App />, root);

useEffect的第一个参数

第一个参数称为effect,是一个函数,它要么返回一个函数(称为cleanup),要么返回undefinedeffect在组件被挂载时(第一次渲染时)被执行,在后续的更新中是否被执行由作为第二个参数传递的依赖关系数组决定。

effect参数的返回值

在上一节中,我们说过,useEffect的第一个参数是一个名为effect的函数。effect不需要参数,它必须返回一个函数或未定义的函数。如果它返回的是一个函数,那么返回的函数被称为cleanupcleanup在调用effect之前被执行(清理之前渲染的效果)。如果你对为什么以及什么时候需要清理很好奇,可以看看React文档中的解释。由于effect返回的要么是函数,要么是undefined,所以没有cleanupeffect并不少见。

useEffect的第二个参数

useEffect的第二个参数是一个依赖关系的数组。如果你想控制在挂载组件后什么时候执行effect,那么就传递一个依赖关系数组作为第二个参数。依赖关系是指定义在useEffect之外,但在useEffect内部使用的值,比如。

     function App(){
         const[state, setState] = useState(0);
          // state is defined here
         useEffect(() => {
              console.log(state); 
              //value of state is used here therefore must be passed as a dependency
         }, [state])

     }

React会比较依赖关系的当前值和之前渲染的值。如果它们不一样,就会调用effect。 这个参数是可选的。如果省略它,effect将在每次渲染后被执行。如果你想让effect只在第一次渲染时执行,你可以传递一个空数组。

     useEffect(() => {
       console.log("Effect has been called");
}, []) // Empty array as dependency, useEffect is invoked once

依赖关系可以是stateprops。需要注意的是,如果要在useEffect内部使用任何定义在useEffect之外、且在组件内部的值,则必须将其作为依赖关系传递。这一点将在下面进行说明。

  function App(props) {
     const [count, setCount] = React.useState(1);
     // count and setCount are defined inside component(App) but outside useEffect
     useEffect(() => {
       //count is being used inside useEffect. Therefore must be passed as dependency.
       console.log(count);
}, [count])
}

传递一个函数作为依赖关系

你可能想知道,如果你在useEffect之外定义了一个函数,然后在effect里面调用它,你需要把它作为一个依赖关系传递给它吗? 举个例子。

 function App(props){
    const [data, setData] = useState(null);
    const fetchData = () => {
         //fetch some data 
    }
    useEffect(() => {
    fetchData(); //Invoked inside useEffect
}, [fetchData])

}

不建议像上面这样,在外面定义一个函数,然后在effect里面调用它。上面的情况会导致每次渲染都会调用fetchData,因为传递的依赖是一个函数,而函数是对象。React会比较上一次和当前渲染的fetchData,两者是不一样的,因此会触发对effect的调用。

根据React文档中关于useEffect hook的部分

很难记住哪些props或state是由effect之外的函数使用的。这就是为什么通常你会希望在effect内部声明它所需要的函数。这样就可以很容易地看到该effect所依赖的组件作用域中的哪些值。
你也可以把函数移到effect内部,这样它就不需要在依赖列表中了。

参考文献