在 React 中用 useEffect Hook 替换组件生命周期

介绍

React Hooks 正在彻底改变我们在 React 中的开发方式并解决我们最关心的一些问题。useEffect挂钩允许我们来取代重复组件的生命周期代码。

本质上,Hook 是一个特殊的函数,它允许你“挂钩”React 特性。如果您之前编写了一个函数式组件并意识到需要向其添加状态,则 Hook 是一个很好的解决方案。

如果你是 Hooks 的新手并且想要一个概述,请查看React Hooks介绍

本文假设您熟悉useStateHook。如果你不是,永远不要害怕!如果您花一点时间使用状态挂钩基于 React 类的组件转换为函数式组件,那么您将走在正确的轨道上!

关于使用效果

useEffect是“使用副作用”的缩写。效果是当我们的应用程序与外部世界做出反应时,就像使用 API 一样。它允许我们根据是否发生变化来运行函数。useEffect还允许我们结合componentDidMountcomponentDidUpdate

关于我们的应用程序

我们将采用一些预先编写的基于类的代码并将其转换为功能组件。我们将使用reactstrap来简化我们的格式和axios来调用外部虚拟 API。

具体来说,我们使用jsonplaceholder在我们的初始组件挂载上拉入虚拟用户数据。

入门代码

然后我们根据用户点击触发组件重新渲染,并拉入有关用户的其他数据。

入门代码,点击

入门

只需使用起始代码克隆 repo:

$ git clone https://github.com/alligatorio/use-effect-hook
$ npm i
$ npm start

花点时间熟悉一下代码,尤其是ClassBasedComponent.js文件。

你会注意到我们在这个文件中有两个生命周期方法,componentDidMountcomponentDidUpdate.

async componentDidMount() {
  const response = await axios
    .get(`https://jsonplaceholder.typicode.com/users`);

  this.setState({ users: response.data });
};

async componentDidUpdate(prevProps) {
  if (prevProps.resource !== this.props.resource) {
    const response = await axios
      .get(`https://jsonplaceholder.typicode.com/users`);

    this.setState({ users: response.data });
  }
};

这些都是async调用jsonplaceholder API 来引入用户列表的生命周期方法

在 中componentDidMount,我们说在第一次渲染时,获取用户数据接下来,componentDidUpdate我们查看props. 这可以由用户发起的事件触发,例如在我们的示例中,按下按钮。一旦检测到变化,我们说,出去再次获取数据

我们想将生命周期方法压缩到useEffectHook 中并创建一个基于函数的组件。

创建组件

不要使用相同的ClassBasedComponent.js文件,而是创建一个名为FunctionBasedComponent.js. 我们正在创建一个新文件,以便我们可以对比和比较两者。

在终端中,您可以运行以下命令从根目录创建新文件:

$ touch FunctionBasedComponent.js

为了帮助开始,请将以下代码复制并粘贴到您的新文件中:

import React, { useState, useEffect } from 'react';
import { Container, Button, Row } from 'reactstrap';
import axios from 'axios';

const FunctionBasedComponent = () => {
  return (
    <Container className="user-list">
      <h1>My Contacts:</h1>
    </Container>
  )
};

export default FunctionBasedComponent;

现在跳到您的App.js文件,导入您的FunctionBasedComponent.js文件并替换ClassBasedComponentFunctionBasedComponent.

您的应用现在应该类似于下面的屏幕截图。

我们的开始 useEffect 应用程序


让我们从用 初始化状态开始useState

const [ users, setUsers ] = useState([]);
const [ showDetails, setShowDetails ] = useState(false);

为了快速回顾一下useStatestateuseState钩子初始化,我们在数组中声明我们的变量和对应于该变量的函数,然后我们传递useState()我们想要用来初始化我们的变量的参数。

  • users状态变量被初始化为空数组和给定的功能setUsers
  • showDetails状态变量被初始化为值false和分配的功能setShowDetails

添加 API 调用

让我们继续添加我们的 API 调用作为fetchUsers函数。

const fetchUsers = async () => {
  const response = await axios.get(`https://jsonplaceholder.typicode.com/users`);

  setUsers(response.data);
};

我们基本上是async从前者componentDidMountcomponentDidUpdate函数中提取这个调用

请记住,我们不能async直接在useEffect. 如果我们想调用一个异步函数,我们需要useEffectuseEffect.

useEffect 参数

让我们先谈谈useEffect钩子。很像componentDidMountuseEffect会立即调用我们的函数。

useEffect( () => {}, [ 'value' ]);

默认情况下,useEffect查看数组值是否不同,如果不同,则自动调用箭头函数。

useEffect( () => {}, [ 'different value' ]);

让我们回到我们的代码编辑器,并useEffect在我们将调用的最新函数下方添加钩子fetchUsers

在下面的代码中,我们正在查看用户对象以查看是否有更改。

useEffect( () => { fetchUsers(users) }, [ users ] );

常见问题

  • 如果您不将数组传递给useEffect Hook,您的组件将不断重复地重新加载。
useEffect( () => { fetchUsers(users) } );
  • 如果你传递一个空数组,我们不会观察任何变量,因此它只会在第一次渲染时更新状态,就像componentDidMount.
useEffect( () => { fetchUsers(users) }, [] );
  • 每次我们在 JavaScript 中创建一个对象时,它在内存中都是一个不同的对象。尽管下面的代码看起来相同,但页面将被重新渲染,因为每个对象都存储在不同的内存地址中。相同的逻辑适用于数组。
useEffect( () => { fetchUsers(users) }, [{ user: 'Alli Alligator' }] );

不等于!

useEffect( () => { fetchUsers(users) }, [{ user: 'Alli Alligator' }] );
  • useEffect函数必须返回一个清理函数或什么都不返回。

要演示触发另一个重新渲染,请将以下代码复制并粘贴到您的FunctionBasedComponent.js文件中:

import React, { useState, useEffect } from 'react';
import { Container, Button, Row } from 'reactstrap';
import axios from 'axios';

const FunctionBasedComponent = () => {
  const [ users, setUsers ] = useState([]);
  const [ showDetails, setShowDetails ] = useState(false);

  const fetchUsers = async () => {
    const response = await axios.get(`https://jsonplaceholder.typicode.com/users`);

    setUsers(response.data);
  };

  useEffect( () => { fetchUsers(users) }, [ users ] );

  const handleClick = event => { setShowDetails(!showDetails) };

  return (
    <Container>
      {
        users.map((user) => (
          <ul key={ user.id }>
            <li>
              <strong>{ user.name }</strong>
              <div>
                <Button
                  onClick={ handleClick }
                >
                  { showDetails ? "Close Additional Info" : "More Info"  }
              </Button>
               { showDetails &&
                 <Container className="additional-info">
                   <Row>
                     { `Email: ${ user.email }` }
                   </Row>
                   <Row>
                     { `Phone: ${ user.phone }` }
                   </Row>
                   <Row>
                     { `Website: ${ user.website }` }
                   </Row>
                 </Container>
               }
              </div>
            </li>
          </ul>
        ))
      }
    </Container>
  )
}

export default FunctionBasedComponent;

现在我们有onClick一个按钮内事件。单击按钮时, 的状态showDetails发生更改,触发重新渲染,该重新渲染将再次调用 API 并引入我们需要的其他详细信息。

瞧!

async componentDidMount() {
    const response = await axios.get(`https://jsonplaceholder.typicode.com/users`)
    this.setState({ users: response.data })
};

async componentDidUpdate(prevProps) {
  if (prevProps.resource !== this.props.resource) {
    const response = await axios.get(`https://jsonplaceholder.typicode.com/users`)
    this.setState({ users: response.data })
  }
};

变成:

const fetchUsers = async () => {
  const response = await axios.get(`https://jsonplaceholder.typicode.com/users`);

  setUsers(response.data);
};

useEffect( () => { fetchUsers(users) }, [ users ] );

觉得文章有用?

点个广告表达一下你的爱意吧 !😁