React Hooks 简明教程(包括数据获取、自定义 Hooks、上下文和用法)
概述
使用状态
使用效果
useContext
钩子规则
定制挂钩
所以,我尝试为那些刚接触 hooks 的人提供一个快速概览,介绍你应该了解的最重要的 hooks,并提供每个 hooks 的基本使用示例,帮助你入门。
现在我们出发。
概述
钩子函数试图解决多个(看似不相关的)问题,但为了尽可能简洁,您应该知道钩子函数允许您:
- 在你的函数组件中设置状态
- 在多个组件中重用一段状态逻辑
- 逻辑过于复杂时,请简化它。
让我们从第一个钩子开始。
使用状态
useState 是 Hooks 中在函数组件中管理状态的方法。例如,假设你要实现一个掌声计数器,通常会通过实现一个基于类的组件来实现,如下所示:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
claps: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.claps} times</p>
<button onClick={() => this.setState({ claps: this.state.claps + 1 })}>
Clap
</button>
</div>
);
}
}
在 Hooks 中,你也可以通过编写以下代码来实现同样的功能:
function Example() {
const [claps, setClaps] = useState(0);
return (
<div>
<p>You clapped {claps} times</p>
<button onClick={() => setClaps(claps + 1)}>
Clap
</button>
</div>
);
}
瞧,你就得到了一个功能齐全的状态(双关语)。
这行代码使用了数组解构,这是ES6const [claps, setClaps] = useState(0);中引入的。
claps是我们的状态字段,它的初始值是我们传递给 `calls` 的任何值useState,在本例中为 `0` 0,因此 `calls` 的初始值将等于 `0`。0
setClaps我们将使用这个函数来修改claps状态字段。正如你所看到的,当我们点击鼓掌按钮时,会触发该函数onClick,并setClaps调用另一个函数,传入现有值claps“加号” 1,该值将成为claps字段的新值。
第一个钩子就此完成!
使用效果
该useEffect钩子可以用来模拟许多现有的生命周期方法,例如 `get_lifecycle()`和componentDidMount` get_lifecycle()` (当然也包括一些较旧的生命周期方法,例如 `get_lifecycle( )`)。componentWillUnmountcomponentDidUpdatecomponentWillRecieveProps
但在查看使用示例之前,您应该知道它useEffect接受两个参数,一个函数和一个数组。
该数组(我们称之为依赖项数组)将包含值的名称,当这些值发生更改时,函数(即第一个参数)将被调用。
如果没有依赖关系数组怎么办?
- 这意味着 useEffect 函数(它的第一个参数)会在每次渲染时运行,这通常不是我们想要的结果。
在后面的例子中,我们会提到当依赖数组为空时会发生什么情况。
对状态/属性变化做出反应
我们来看一个例子。
假设你想在用户每次鼓掌时执行某些操作,例如,你想显示console.log“你鼓掌了”的消息,useEffect我们可以用钩子函数来实现这一点。
function Example() {
const [claps, setClaps] = useState(0);
useEffect(()=>{
console.log("You clapped");
},[claps])
return (
<div>
<p>You clapped {claps} times</p>
<button onClick={() => setClaps(claps + 1)}>
Clap
</button>
</div>
);
}
所以,这里发生的情况是,每次状态字段发生变化时,React 都会检查我们所有的 useEffect(是的,我们的代码中可以有多个 useEffect,就像可以使用 `useEffect` 定义多个状态字段一样useState),并触发所有useEffect依赖数组中包含已更改字段的函数。
所以,在我们的例子中,当我们点击鼓掌按钮时,该setClaps函数会被调用,claps状态字段会发生变化,这会导致 useEffect 的第一个参数(即它的函数)被调用,因为它的依赖数组包含该claps字段。
...
useEffect(()=>{
console.log("You clapped");
},[claps])
...
所以,这基本上就是模拟的方法。componentDidUpdate
当然,这种结构也可以用于在 prop 更改时调用 useEffect hook 函数,只需将您希望考虑的任何 prop 添加到依赖项数组中即可。例如,如果拍手次数是从组件的父组件获取的:
function({claps}){
useEffect(()=>{
console.log("You clapped");
},[claps])
...
}
另外,由于它被称为依赖数组,请记住它可以包含多个值,并且当依赖数组中的一个或多个值发生更改时,该函数将被触发。
数据获取
useEffect 也可以用来获取数据,但在使用此钩子获取数据之前,需要问的关键问题是:
如果依赖项数组为空会怎样?
- 这意味着钩子不会在每次渲染时运行,因为我们明确告诉它不要监视任何变量,所以它只会在挂载时运行。
这通常是我们想要获取数据时所需要的。
既然我们已经知道如何让 useEffect 只在组件挂载时运行(模拟 componentDidMount 生命周期),那么获取数据就变得非常简单,只需这样做:
function App(){
const [data,setData] = useState([]);
useEffect(()=>{
const data = await axios('https://datafetchingexample/data');
setData(data);
},[])
...
}
清理
接下来,我们需要弄清楚如何使用useEffect它在组件中执行任何我们想要进行的清理工作。
function App(){
const [data,setData] = useState([]);
useEffect(()=>{
const source = axios.CancelToken.source();
const data = await axios('https://datafetchingexample/data');
setData(data);
return () => {
source.cancel();
};
},[])
...
}
正如您可能已经注意到的,我们在钩子函数中添加了一个返回值,该返回函数将在组件卸载时运行,使其成为进行任何清理工作(关闭套接字、取消订阅、取消请求等……基本上与 componentWillUnMount 的用法相同)的理想位置。
useContext
接下来,我们将探讨如何使用上下文和钩子。
如你所知,Context 是 React 用于管理组件间状态的一种方式,它本质上是 React 自己的 Redux。
当你在组件中有一些数据,并且希望该组件的后代(直接子组件或一般的间接子组件)能够访问这些数据时,就会用到上下文。例如,假设我们有一个组件获取数据,并且你想将这些数据传递给你的子组件,很显然的方法是使用 props,但是如果你想让你的子组件(甚至更远的子组件)也能访问这些数据……这时使用 props 就会变得很麻烦,而使用上下文则更有意义。
为了便于解释,假设你想把这些数据传递给你的直系子女。
首先,我们创建一个值为空对象的上下文。const DataContext = React.createContext({});
接下来,你应该将要传递上下文的组件包裹起来。我们<DataContext value=somevalue></DataContext>
对子组件就是这么做的。我们只需要通过 value 属性确定上下文的值
(在本例中,我们要传递数据字段),所以我们这样做了。
...
const DataContext = React.createContext({});
function App(){
const [data,setData] = useState([]);
useEffect(()=>{
const source = axios.CancelToken.source();
const data = await axios('https://datafetchingexample/data');
setData(data);
return () => {
source.cancel();
};
},[])
return (
<DataContext value={{data}}>
<Child/>
</DataContext>
)
}
现在来看我们的子组件,我们只需要使用useContext钩子函数,将我们需要的上下文对象传递给它,然后获取我们在value属性中添加的数据即可。
...
function Child(){
const {data} = useContext(DataContext);
return (
<ul>
data.map(v=>{
return (
<li>
{v.value}
</li>
)
})
</ul>
)
}
既然我们已经介绍了三种最常用的鱼钩,接下来让我们谈谈鱼钩的一般规则。
钩子规则
只在最高层调用钩子
这意味着你不应该在循环、if 条件语句或嵌套函数中使用 Hooks,而应该始终在 React 函数的顶层使用。这是因为 Hooks 的执行依赖于它们的初始化顺序。例如,如果你在 if 条件语句中将另一个 Hooks 嵌套在另一个 Hooks 中,那么该 if 条件语句在下一次渲染时可能不会执行,从而导致 Hooks 执行混乱。我们将在另一篇文章中详细讨论这个问题。
不要在 JavaScript 函数中调用钩子函数
你可以从两个地方呼叫钩子
- React 功能组件
- 接下来我们将讨论自定义钩子。
定制挂钩
现在来说说 React Hooks 的最后一个也是核心部分:创建自定义 Hooks。
自定义钩子是名称以 `.hook` 开头的 JavaScript 函数use,它能够调用其他钩子(自定义的或内置的)。
构建自定义钩子意味着您可以提取一段逻辑,以便在多个地方使用。例如,假设您有两个输入框,分别接受姓名和年龄。
function InputForm(){
const [name,setName] = useState("")
const [age,setAge] = useState(0)
return (
<div>
<input type="text" placeholder="Enter your name" value={name} onChange={(e)=>setName(e.target.value)/>
<input type="number" placeholder="Enter your age" value={age} onChange={(e)=>setAge(e.target.value)}/>
</div>
)
}
现在,我们应用中的所有输入框基本上都具有类似的结构:包含值和 onChange 属性的输入字段。不仅在这个文件中,我们还可以在多个文件中使用状态来处理输入。自定义钩子允许我们提取可重用的逻辑并在其他地方使用。
它看起来大概是这样的:
function useFormInput(initialValue){
const [value,setValue] = useState(initialValue);
function onChange(e){
setValue(e.target.value);
}
return {value,onChange};
}
function InputForm(){
const name = useFormInput("")
const age = useFormInput(0)
return (
<div>
<input type="text" placeholder="Enter your name" {...name} />
<input type="number" placeholder="Enter your age" {...age} />
</div>
)
}
更简洁吧?这和常规方法的效果一样,但现在你有了可重用的钩子,可以在应用程序的任何位置使用功能可变的输入框。只需使用钩子,解构输入标签中返回的值,就可以了!
如果你觉得在完成 4 个项目的同时还需要更多练习,我建议你看看这门课程:
https://www.udemy.com/course/react-hooks-projects-course/
文章来源:https://dev.to/bitpunchz/react-hooks-in-a-nutshell-4llg





