JavaScript / 前端 / 译文 · 10月 7, 2020 0

React v16.8.x – Hooks 概述

hooks 是向后兼容的。此页面提供了有经验的React用户对Hooks的概述。这是一个快节奏的概述。

state Hooks

此示例呈现计数器。单击该按钮时,它会递增值:

import React, { useState } from 'react';

function Example() {
  // 声明一个state:count ,初始值是0
  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 返回一对值:当前的状态值和更新状态的函数。你可以在时间处理程序或其他地方使用这个函数。看起来和类组件中的 this.setState ,但是没有把新旧状态合并在一起。后续我们会比较两者的差异。
useState 仅有的参数是状态的初始值。 在上面的例子中,计数器从零开始,所以是 0。注意,和 this.state 不同,这个状态不必是对象,可以是任何你想要的值。状态的初始值仅在第一次渲染时生效。

定义多个状态变量

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

function ExampleWithManyStates() {
  // 多个状态
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}

数组解构语法让我们可以用 useState 声明多个不同名字的状态变量。这并不是 useState API设计的一部分。相反,React做的只是假定如果多次调用useState,则在每次渲染期间以相同的顺序执行。我们将回到为什么这种方法有效,以及何时有用。

Effext Hook

你之前可能已经从React组件执行数据提取,订阅或手动更改DOM。我们称这些操作为“副作用”,因为它们会影响其他组件,并且在渲染过程中无法完成。

Effect Hook,useEffect,可以让函数组件执行副作用。它和类组件中的 componentDidMountcomponentDidUpdatecomponentWillUnmount行为类似,但是统一成一个API。

例如,在React更新DOM之后,此组件设置文档标题:

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // 与 componentDidMount 和 componentDidUpdate 类似
  useEffect(() => {
    // 更新文档标题
    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函数。effect 在组件内声明,因此可以访问其props和state。默认情况下,React在每次渲染后运行 —— 包括第一次渲染。

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

import React, { 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';
}

在这个例子中,React会在组件卸载时或者由于后续渲染而重新运行effect之前,
通过ChatAPI取消订阅。如果我们传递给ChatAPIprops.friend.id没有改变,也有办法告诉React跳过重新订阅。

就像使用useState一样,您可以在组件中使用多个useEffect

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 规则

Hooks 是JS函数,但是需要遵循两条额外的规则:

  • 在顶层使用Hooks。 不要在循环,条件或嵌套函数中调用Hook。
  • 仅在React函数组件中使用Hooks。 不要在普通JavaScript函数中调用Hook。

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

组织你自己的 Hooks

有时,我们需要在组件间重用状态逻辑。一般有两种方式:HOC高阶函数,渲染props。Hooks可以在不为组件树添加新组件的情况下解决这个问题。

在本页前面,我们介绍了一个调用useStateuseEffect Hooks的FriendStatus组件来订阅朋友的在线状态。假设我们还希望在另一个组件中重用此订阅逻辑。

首先,我们将这个逻辑提取到一个名为useFriendStatus的自定义Hook中:

import React, { 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两次。
消费Hooks更像是一种约定,而不是一种功能。如果函数的名称以“use”开头并且它调用其他Hook,我们说它是一个自定义Hook。useSomething命名约定是我们的linter插件能够使用Hooks在代码中查找错误的前提。

你可以编写自定义Hook,涵盖各种用例,如表单处理,动画,声明订阅,计时器,以及可能还有更多我们未考虑过的用例。我们很高兴看到React社区提出的定制Hooks。

其他 Hooks

有一些不太常用的内置Hook可能对你很有用。例如,useContext允许您订阅React context而不引入嵌套:

function Example() {
  const locale = useContext(LocaleContext);
  const theme = useContext(ThemeContext);
  // ...
}

useReducer允许您使用reducer管理复杂组件的本地状态:

function Todos() {
  const [todos, dispatch] = useReducer(todosReducer);
  // ...
}
冀ICP备19028007号