了解React UseEffect和创建自定义钩子
React钩子是一些特殊的函数,允许你 "钩住 "React功能而不需要创建类。它们为编写基于类的组件提供了一种替代方案(因为钩子在类里面没有功能)。
通过React组件获取数据或手动改变文档对象模型(DOM)等操作可能会导致副作用或 "效应",因为它们是众所周知的。这可能会影响你应用程序中的其他组件。
React带有内置的钩子,如useEffect ,允许你从一个函数或组件执行副作用。通过一个效果函数,我们可以实现React类的目的。这包括componentDidMount,componentWillUnmount, 和componentDidUpdate 。
主要收获
在本教程结束时,读者将能够理解。
- 什么是React钩子以及它们的好处。
useEffect的功能。- 使用
useEffect时的规则。 - 如何用
useEffect消费API并处理响应。 - 如何使用
useEffect,通过返回一个函数来 "清理 "效果(或我称之为 "后效果")。
前提条件
要完成这篇文章,读者应该有。
- 安装了React v16.0或更新版本。
- 对Javascript的理解。
- 有React.js的基本知识。
- 一个你选择的合适的文本编辑器。
什么是React钩子?
React钩子是React开发者在很长一段时间内发生的最好的事情。它使功能性程序员有可能创建动态项目。
这发生在不必写类的情况下,因为基于类的组件需要一个render() method ,有复杂的UI逻辑,一般来说管理起来更复杂。你创建一个钩子取决于它的效用。
什么是React UseEffect?
useEffect钩子有超强的能力,使我们能够设计我们的自定义钩子。当一个变化发生时,它允许我们在功能组件中执行副作用。它允许在每次组件渲染时进行数据检索、DOM修改和函数执行。
让我们来探讨一些例子。
import React, { useState, useEffect } from "react";
import "./Counter.css";
export const Counter = () =\> {
const [count, setCount] = useState(0);
// useEffect hook used to log and display the number of times counter is updated
useEffect(() =\> {
console.log("counter ran once");
}, []);
return (
\<div className="modal"\>
\<div className="modal\_\_counter"\>
\<div
className="modal\_\_counter--decrease"
onClick={() =\> setCount(count - 1)}
\>
-
\</div\>
\<div className="modal\_\_counter--reset"\>{count}\</div\>
\<div
className="modal\_\_counter--increase"
onClick={() =\> setCount(count + 1)}
\>
+
\</div\>
\</div\>
\</div\>
);
};
上面的代码是一个简单的计数器,允许用户增加和减少一个值。
让我们用下面的代码给它添加一些样式。
.modal\_\_counter {
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #dbdada;
border-radius: 5px;
width: 100%;
max-width: 100px;
margin-top: 0.5rem;
overflow: hidden;
}
.modal\_\_counter--increase,
.modal\_\_counter--decrease {
background-color: #f2f2f2;
padding: 0 0.7rem;
cursor: pointer;
}
.modal\_\_counter--reset {
padding: 0 0.5rem;
cursor: pointer;
}
.modal-body\_\_date,
.modal-body\_\_time {
font-weight: 400;
padding-top: 2rem;
}
我们使用useState 钩子来更新上述代码中的counter 变量。每次我们打算改变count 的值时,都会触发useEffect 函数。这样就可以追踪到对该组件所做的任何改变。
在这个例子中,页面的初始加载触发了useEffect 函数。

这就是前面代码的输出。

上面的输出显示了counter 的值。组件的初始渲染导致该函数的运行。
让我们修改代码,改变数组的依赖性,目前是空的,然后反映counter 的当前状态。
import React, { useState, useEffect } from "react";
import "./Counter.css";
export const Counter = () =\> {
const [count, setCount] = useState(0);
//useEffect hook used to log and display the number of times counter is updated
useEffect(() =\> {
console.log("counter ran once");
}, [count]); //current status of count is used to update component
return (
\<div className="modal"\>
\<div className="modal\_\_counter"\>
{/\* subtraction button to reduce the value of counter \*/}
\<div
className="modal\_\_counter--decrease"
onClick={() =\> setCount(count - 1)}
\>
-
\</div\>
{/\* counter value is displayed \*/}
\<div className="modal\_\_counter--reset"\>{count}\</div\>
{/\* addition button to increase the value of counter \*/}
\<div
className="modal\_\_counter--increase"
onClick={() =\> setCount(count + 1)}
\>
+
\</div\>
\</div\>
\</div\>
);
};
接下来,我们将counter 的值递增三次。这样,每当用户点击增量或减量按钮时,useEffect ** 函数就会运行并向控制台发送一个结果。

这告诉我们,我们的counter 变量已经发生了变化。只要该变量发生变化,它就会一直持续下去。

当页面第一次加载时,计数器开始计数。它被增加了三次,总共运行了四次。
使用'useEffect'的规则
我们使用useEffect 钩子来在组件的生命周期内运行功能,而不是特定的用户交互或DOM事件。
例如,你可能希望在页面加载时立即获得一个用户列表。这些人的名字会随着组件的加载而改变,而不需要用户互动。
建议你在异步操作中使用useEffect 。这有助于避免不必要的错误,这些错误可能导致你的用户界面无法使用。
如何用useEffect消耗API并处理响应
现在我们对useEffect 有了一些了解,让我们用API获取一些数据。我们将使用JSON占位符自由API,一个标准的API来处理假数据。
import React, { useEffect, useState } from "react";
import axios from "axios"
export const Users = () =\> {
const [names, setNames] = useState([]);
//We make an asynchronous API call using the useEffect hook
useEffect(() =\> {
const getAllUsers = async () =\> {
//The actual API call is made within the try block
try {
const res = await
//A GET request is sent to retrieve "users"
axios.get("[https://jsonplaceholder.typicode.com/users](https://jsonplaceholder.typicode.com/users)");
//We set names using the data from the response of the API call
setNames(res.data);
} catch (err) {
console.log(err);
}
};
getAllUsers(); //component unmounts
}, []);
//List of users is then displayed
return (
\<div\>
\<h1\>\<b\>List of Users\</b\>\</h1\>
\<br /\>
{ names.map((name) =\> (
\<div key={ name.username }\>
\<h2\>{ name.name }\</h2\>
\<br /\>
\<hr /\>
\</div\>
)
)}
\</div\>
);
};
在上面的例子中,我们使用了useState 和useEffect ,这是两个不同的钩子。我们使用useState 变量来调节API的响应,并采用useEffect 来检索数据。
我们使用try-catch 函数来调节获得的请求是成功还是失败。我们导入axios,它被用来向API发出get ****请求。
我们收到了结果,并将其传递给setState ,以映射到可用的用户列表中。

我们上面使用的请求返回了结果,我们可以在上面的图片中看到。在我们结束之前,让我们看一下最后的关键点。
如何使用useEffect来 "清理 "效果
下面是一个典型的错误,需要在useEffect 中使用清理函数。
| 警告。不能在一个未挂载的组件上执行React状态更新。这是一个无用功,但它表明你的应用程序中存在内存泄漏。为了解决这个问题,在useEffect清理函数中取消所有的订阅和异步任务。 |
|---|
为了修复这个错误,我们可以给我们的应用程序添加一个清理方法,如下图所示。
import React, { useEffect, useState } from "react";
import axios from "axios"
export const Users = () =\> {
const [names, setNames] = useState([]);
//We make an asynchronous API call using the useEffect hook
useEffect(() =\> {
const getAllUsers = async () =\> {
//The actual API call is made within the try block
try {
const res = await
//A GET request is sent to retrieve "users"
axios.get("[https://jsonplaceholder.typicode.com/users](https://jsonplaceholder.typicode.com/users)");
//We set names using the data from the response of the API call
setNames(res.data);
} catch (err) {
console.log(err);
}
};
getAllUsers(); //component unmounts
//clean up function is added
return () =\> {
console.log("I am inside a cleanup function");
};
}, []);
//List of users is then displayed
return (
\<div\>
\<h1\>\<b\>List of Users\</b\>\</h1\>
\<br /\>
{ names.map((name) =\> (
\<div key={ name.username }\>
\<h2\>{ name.name }\</h2\>
\<br /\>
\<hr /\>
\</div\>
)
)}
\</div\>
);
};
在上面的代码中,清理函数在useEffect 函数的第二个变化后运行。我们使用清理来中止异步操作,通常是在组件更新或卸载后的第二次渲染。
如何创建一个自定义钩子
我们可以通过创建我们的自定义钩子来构建可以在我们的应用程序中重复使用的逻辑。它产生了很多可重用的功能。
首先,让我们在我们的src 目录中创建一个文件夹,并将其命名为hooks ,然后我们在hooks 文件夹中创建一个名为CounterHook.jsx 的文件。
将下面的代码添加到CounterHook.jsx 文件中。
import { useState } from "react";
export const useCounter = () =\> {
const [counter, setCounter] = useState(0);
const increment = () =\> setCounter(counter + 1);
const decrement = () =\> setCounter(counter - 1);
return { counter, increment, decrement };
};
我们正在使用useState来创建与我们在第一个counter 应用程序中使用的相同的逻辑,但这一次,该逻辑是在一个可重用的函数中。
接下来,让我们把这个函数添加到我们的counter 应用程序中。
import React from "react";
import "./Counter.css";
import { useCounter } from "../../hooks/CounterHook";
export const Counter = () =\> {
const { counter, increment, decrement } = useCounter();
return (
\<div className="modal"\>
\<div className="modal\_\_counter"\>
\<div className="modal\_\_counter--decrease" onClick={decrement}\>
-
\</div\>
\<div className="modal\_\_counter--reset"\>{counter}\</div\>
\<div className="modal\_\_counter--increase" onClick={increment}\>
+
\</div\>
\</div\>
\</div\>
);
};
我们的应用程序并没有坏,事实上,它处于完美的工作状态我们将CounterHook 组件作为一个钩子导入,我们也可以在任何其他程序中使用。我们创建的自定义钩子提高了我们应用程序的效率。
根据你的应用程序的上下文,创建自定义钩子应该取决于个人的喜好。
总结
我们已经了解了什么是钩子,它们如何工作,以及它们的好处。我们还演示了useEffect如何管理组件的副作用。
最后,我们已经能够用简单的逻辑创建一个可重用的自定义钩子。通过ReactuseEffect 钩子,你可以无缝地管理组件的生命周期,而不一定要把你的基于功能的组件转换成基于类的组件。