介绍
React Hooks 正在彻底改变我们在 React 中的开发方式并解决我们最关心的一些问题。该useEffect
挂钩允许我们来取代重复组件的生命周期代码。
本质上,Hook 是一个特殊的函数,它允许你“挂钩”React 特性。如果您之前编写了一个函数式组件并意识到需要向其添加状态,则 Hook 是一个很好的解决方案。
如果你是 Hooks 的新手并且想要一个概述,请查看React Hooks的介绍。
本文假设您熟悉useState
Hook。如果你不是,永远不要害怕!如果您花一点时间使用状态挂钩将基于 React 类的组件转换为函数式组件,那么您将走在正确的轨道上!
关于使用效果
useEffect
是“使用副作用”的缩写。效果是当我们的应用程序与外部世界做出反应时,就像使用 API 一样。它允许我们根据是否发生变化来运行函数。useEffect
还允许我们结合componentDidMount
和componentDidUpdate
。
关于我们的应用程序
我们将采用一些预先编写的基于类的代码并将其转换为功能组件。我们将使用reactstrap来简化我们的格式和axios来调用外部虚拟 API。
具体来说,我们使用jsonplaceholder在我们的初始组件挂载上拉入虚拟用户数据。
然后我们根据用户点击触发组件重新渲染,并拉入有关用户的其他数据。
入门
只需使用起始代码克隆 repo:
$ git clone https://github.com/alligatorio/use-effect-hook
$ npm i
$ npm start
花点时间熟悉一下代码,尤其是ClassBasedComponent.js
文件。
你会注意到我们在这个文件中有两个生命周期方法,componentDidMount
和componentDidUpdate
.
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
. 这可以由用户发起的事件触发,例如在我们的示例中,按下按钮。一旦检测到变化,我们说,出去再次获取数据。
我们想将生命周期方法压缩到useEffect
Hook 中并创建一个基于函数的组件。
创建组件
不要使用相同的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
文件并替换ClassBasedComponent
为FunctionBasedComponent
.
您的应用现在应该类似于下面的屏幕截图。
让我们从用 初始化状态开始useState
。
const [ users, setUsers ] = useState([]);
const [ showDetails, setShowDetails ] = useState(false);
为了快速回顾一下useState
,state
用useState
钩子初始化,我们在数组中声明我们的变量和对应于该变量的函数,然后我们传递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
从前者componentDidMount
和componentDidUpdate
函数中提取这个调用。
请记住,我们不能async
直接在useEffect
. 如果我们想调用一个异步函数,我们需要useEffect
在useEffect
.
useEffect 参数
让我们先谈谈useEffect
钩子。很像componentDidMount
,useEffect
会立即调用我们的函数。
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 ] );