Libx

Hooks at a Glance

字数统计: 1,970阅读时长: 8 min
2018/10/28 Share

在看了React Hooks的文档之后,本英语渣渣竟然突然有了想要翻译一下这篇文档的冲动,于是就首次尝试翻译一篇英文文档,原文Hooks at a Glance

Hooks是一个React中的新提案,他可以让你在不使用Class的情况下使用state和其他的React特性。Hooks 目前已经发布在 v16.7.0 alpha 版本,且正在开放的RFC讨论。

Hooks是向后兼容的,这篇文档将向有经验的React开发者提供一个概览。

📌 State Hook

这是一个计数器的例子,点击按钮,数字加一。

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返回一个pair:包含了当前的状态值和一个可以用来更新该值的函数。你可以在一个事件监听器或者其他地方调用这个函数。它和Class中的setState是类似的,不过他没把新旧状态合并在一起。(稍后会有例子来比较在使用State Hooks时useState和this.state的差异)

useState只有一个参数作为他的初始状态,在上面的例子中,这个参数是0,因为计数器从0开始。请注意,和this.state不同的是,这里的state不一定非要是一个对象——你要是想那也行。初始状态只在首次渲染时使用。

声明多个状态变量

你可以在一个组件中多次使用State Hook

function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}

数组解构语法允许我们为通过调用useState声明的状态变量赋予不同的名称,这些名字并不是useState这个API的一部分,相反,React假定如果多次调用useState,就在每次渲染期间以相同的顺序执行。我们稍后会讨论这什么时候有用。

但到底什么是Hook呢?

Hooks 是一个React 函数组件内一类特殊的函数(通常以 “use” 开头,比如 “useState”),使开发者能够在 function component 里 ‘hook in’ state 和 life-cycles,以及使用 custom hook 复用业务逻辑。Hooks在Class组件中不能使用-这使得你可以在不写Class的情况下使用React(我们不建议您在一夜之间重写现有组件,但如果您愿意,可以开始在新组件中使用Hook。)
React提供了一些像useState这样的内置Hook。你还可以创建custom Hook以在不同组件之间重用有状态行为。我们先来看看内置的Hooks。

⚡️ Effect Hook

你之前可能在React组件执行数据获取,订阅或手动更改DOM。我们称这种操作为“side effects”因为它们会影响其他的组件,并且不会在渲染期间就完成。

Effect Hook:useEffect为函数组件增添了执行side effects的能力。它与React Class中的componentDidMount,componentDidUpdate和componentWillUnmount具有相同的用途,但统一为单个API。 (我们将在使用Effect Hook时显示将useEffect与这些方法进行比较的示例。)

举个栗子,这个组件在React更新DOM之后设置文档标题

import { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and 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是声明在Component内的,所以有权访问组件的props和state。默认情况下,React在每次render都会调用effects,包括第一次render。(我们将更多地讨论使用Effect Hook和class中的lifecycles进行比较。)

Effects还可以通过返回函数指定如何“清理”它们。例如,这个组件使用Effect来订阅朋友的在线状态,并通过取消订阅来清理:

import {useState,useEffect} from 'react'
function FriendStatus(props){
const [isOnline,setIsOnline] = useState(null)
function handleStatusChange(status){
setIsOnline(status.isOnline)
}
useEffects(()=>{
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
})
if(isOnline === null){
return 'loading'
}
return isOnline ? 'Online' :'Offline'
}

在这个例子中,React会在组件unmount时,以及在由于后续渲染而重新运行effects之前取消订阅ChatAPI(你也可以告诉React跳过重新订阅如果我们传给ChatAPI的props.friend.id没有变的话)

就像·useState·一样,你可以在一个Component中多次使用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允许您通过哪些部分相关(例如添加和删除订阅)来组织组件中的side effects,而不是基于生命周期方法强制拆分。
您可以在这个页面上了解有关useEffect的更多信息:Using the Effect Hook

✌️ Rules of Hooks

Hooks是JavaScript函数,但它们强加了两个额外的规则:

  • 只能在顶层调用Hooks。不要在循环,条件或嵌套函数中调用Hook。
  • 仅从React函数组件中调用Hooks。不要从常规JavaScript函数中调用Hook。 (还有另一个有效的地方叫Hooks - 你自己的定制Hooks。我们马上就会了解它们。)

我们提供了一个linter插件来自动执行这些规则。这些规则最初可能看起来有限或令人困惑,但它们对于使Hooks运行良好至关重要。

💡 Custom Hooks

有时,我们希望在组件之间重用一些有状态逻辑。以往,这个问题有两种流行的解决方案:HOC和render props。Custom Hook可以做到这些,并且让你不需往组件树添加更多的组件。
在前面,我们介绍了一个调用useState和useEffect Hooks的FriendStatus组件来订阅朋友的在线状态。假设我们还希望在另一个组件中重用此订阅逻辑:
首先,我们将这个逻辑提取到一个名为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都有一个完全隔离的state - 所以你甚至可以在一个组件中使用相同的自定义Hook两次。
Custom Hooks更像是一种约定而非功能,如果一个函数的名字以‘use’开头,把那个且他调用了其他的Hooks 我们称他为custom Hook 。useSomething命名约定也是linter插件在使用了hooks的代码中查找bug的原理。
您可以编写自定义Hook,涵盖各种用例,如表单处理,动画,声明性订阅,计时器,可能还有更多我们没有考虑过的。我们很高兴看到React社区提出的定制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);
// ...

这只是React 官网中关于Hooks的一篇大概的介绍,更加详细的使用文档,可以在官方文档中获得:HOOKS(Proposal)

CATALOG
  1. 1. 📌 State Hook
    1. 1.1. 声明多个状态变量
    2. 1.2. 但到底什么是Hook呢?
  2. 2. ⚡️ Effect Hook
  3. 3. ✌️ Rules of Hooks
  4. 4. 💡 Custom Hooks
  5. 5. 🔌 Other Hooks