概述
hooks 是一个让你在不使用类的情况下还能使用state和别的一些react功能的新功能(React v16.7.0-alpha版本)
hooks是向后兼容的,这篇文章给有React使用经验的小伙伴提供了一个概述。
下面这个例子定义了一个计数器,当点击按钮的时候。计数器就会加1
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
这上面的例子中useState 就是一个hook,我们在函数里面调用它来添加一些局部的状态。react在重新渲染的时候保存这个状态值,
useState 返回了一个状态值(count)和一个能改变状态值的函数(setCount),你可一再事件或者别的地方来调用这个函数。它除了没有把新的状态值和旧的状态值合并以外别的和class里面的this.setState()都很相似(这里有个例子比较useState和this.state())
useState 唯一的参数是初始化的值。就像上面的例子中,初始化的值是0。
注意:和this.state不一样、这里的state你不必非得定义成一个对象。当然你也可以把它定义成一个对象,初始值只会在第一次被渲染的时候用到
定义多个状态变量
你可以在单个组件里多次使用State hook
function ExampleWithManyStates() {
// 定义多个state变量
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}
数组的解构语法可以让你在调用useState的时候给satate变量定义不同的名字,这些名字不是useState API的一部分。相反的,React假设你每一次渲染的时候多次调用了useState,而每次渲染都是相同的顺序执行.我们会回想一下为什么这样做,当这对以后有用
什么是Hook
钩子是允许从功能组件(function component)“挂钩”React状态和生命周期功能的功能。钩子在类内部不起作用 - 它让你在不依赖类的去情况下使用React(我们不推荐你马上重写已经存在的组件,但是你可以尝试在新的组件里使用Hook)
React 提供了一些内置的Hooks,比如useState。你也可以创建自己的Hooks以在不同组件中重用
Effect Hook
你之前可能是从React组件里执行数据提取,订阅或手动更改DOM。我们将这些操作称为“函数副作用”(或简称为“效果”),因为它们会影响其他组件,并且无法在渲染过程中完成。
Effect Hook中的useEffect增加了在功能组件函数副作用的功能。它与React类中的componentDidMount,componentDidUpdate和componentWillUnmount具有相同的目的,但是将这些钩子统一为单个API。(useEffect与Class钩子函数比较。)
例如,组件在React更新了Dom后设置了文档标题
import { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 和componentDidMount ,componentDidUpdate很相似
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
当你调用useEffect,就是在告诉React在刷新DOM后调用你的'effect'函数,Effects 被定义在组建内容, 这样就可以访问到组件的props和state,默认情况下,React每一次执行render后(包括第一次render)都会执行effects(进一步讨论useEffect和Class生命周期的比较)
Effects也可以指定返回函数来清理状态,下面这个例子,组件使用effect 订阅了好友的在线状态,又通过unsubscribing 来清理状态。(Effects的返回函数相当于componentwillmount)
import { useState, useEffect } from 'react';
function FriendStatus(props) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
在这个例子里面,当组件将被卸载以及重新运行effect导致的渲染之前,React会取消对ChatApi的订阅(如果有需要,在props.friend.id的值没有改变的情况下, 我们可以告诉React跳过重新订阅)
就像useState一样, 你可以在组建中多次使用effect
function FriendStatusWithCounter(props) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
Hooks允许你通过哪些部分相关(例如添加和删除订阅)来组织组件中的函数副作用,而不是基于生命周期方法强制拆分
Hooks的拆分
钩子是js函数,但它们加入了两个额外的规则:
- 只能在顶层调用Hooks。不要在循环语句,条件语句或嵌套函数里调用Hook
- 只能React功能组件调用Hooks。 不能从普通JavaScript函数中调用Hook。 (还有一个地方能合法的使用Hooks - 你自己的定制Hooks。)
构建你自己的Hooks
有的时候,我们希望在组件之间重用一些有状态逻辑。一般来说,这个问题有两种流行的解决办法:高阶组件和render props。Custom Hooks允许你执行这样的操作,并且不需要在代码树中添加更多组件。
在文章的早些部分,我们介绍了一个FriendStatus 组件调用useState和useEffect Hooks来订阅朋友的在线状态。如果我们还想要在另一个组件中重用此订阅逻辑。
首先,我们将这个逻辑提取到一个名为useFriendStatus的自定义Hook中:
import { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
这个组件接受一个friendID 作为参数,然后返回我们的朋友是否在线。
现在我们就可以在下面的两个组件中使用它
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);
return (
<li style={{ color: isOnline ? 'green' : 'black' }}>
{props.friend.name}
</li>
);
}
这些组件的状态是完全独立的,Hooks是重用状态逻辑的一种方式,而不是状态本身。事实上,每一次Hook调用都是一个完全单独的状态 - 所以你甚至可以在一个组件中多次定义一个相同的Hook。
custom hook更像是一种惯例而非功能。如果函数的名字以use开头并且它调用其他Hook,我们就称它为一个Custom Hook。useSomething命管理是linter插件如何使用Hooks在代码中查找错误。
Other Hooks
你可能会发现一些很少用的内置Hook很有用。例如,useContext允许订阅React上下文而不引入嵌套:
function Example() {
const locale = useContext(LocaleContext);
const theme = useContext(ThemeContext);
// ...
}
useReducer允许使用reducer管理复杂组件的本地状态:
function Todos() {
const [todos, dispatch] = useReducer(todosReducer);
// ...
下一步
从 State Hook 文开始吧
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。