使用 State(状态) Hook

Hooks 是一项新功能提案,可让您在不编写类的情况下使用 state(状态) 和其他 React 功能。它们目前处于 React v16.7.0-alpha 中,并在 一个开放RFC 中进行讨论。

介绍 Hooks 中用这个例子介绍了 Hooks :

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>
  );
}

我们将通过将这段代码与一个等效的类示例进行比较来学习 Hooks。

等效的类示例

如果您之前在 React 中使用过 classes(类),那么这段代码应该看起来很熟悉:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

状态以 { count: 0 } 开始,当用户通过单击按钮调用 this.setState()时,我们会增加state.count 。我们将在整个页面中使用这个类的片段。

注意

您可能想知道为什么我们在这里使用计数器而不是更现实的例子。这是为了帮助我们专注于API,同时我们仍在使用 Hooks 迈出第一步。

Hooks 和 函数式组件

提醒一下,React 中的函数组件如下所示:

const Example = (props) => {
  // You can use Hooks here!
  return <div />;
}

或者是这样的:

function Example(props) {
  // You can use Hooks here!
  return <div />;
}

您可能以前将这些称为“无状态组件”。 我们现在介绍的这些组件将具有 React state(状态) 的能力,所以我们更喜欢称他为 “函数式组件”。

Hooks 在 classes(类) 内 起作用。 但是你可以使用它们来取代编写类。

什么是 Hook ?

我们的新示例首先从 React 导入 useState Hook 开始:

import { useState } from 'react';

function Example() {
  // ...
}

什么是 Hook ? Hook是一种特殊函数,可让你 “接入” React 功能。 例如,useState 是一个 Hook,允许您将 React state(状态) 添加到函数式组件中。 我们稍后会学习其他的 Hooks。

我什么时候使用 Hook? 如果你编写一个函数式组件并意识到你需要为它添加一些 state(状态) ,那么之前你必须将它转换为一个 classes(类) 。 但是现在,您可以在现有函数式组件中使用 Hook 。 我们现在要做到这一点!

注意:

关于在何处可以使用 Hook 并且不能在组件中使用 Hook ,有一些特殊规则。 我们将在 Hooks 规则 中学习这些特殊规则。

声明一个 state(状态) 变量

在类中,我们通过在构造函数中将 this.state 设置为 { count: 0 } 来将 count state(状态) 初始化为 0

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

在函数式组件中,我们没有 this ,所以我们不能分配或读取 this.state 。相反,我们可以直接在组件内部调用 useState Hook:

import { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

调用 useState 有什么作用? 它声明了一个 “state(状态)变量”。 我们的变量叫做 count,但我们可以称之为其他任何东西,比如 banana。 这是一种在函数调用之间“保留”某些值的方法 - useState 提供了和使用在类中 this.state 完全相同功能的新方法。 通常,当函数退出时变量就会“销毁”,但 React 会保留 state(状态) 变量。

我们传递给 useState 的参数是什么? seState() Hook 惟一参数是初始 state(状态) 。与 classes(类) 不同,这里的 state(状态) 不一定是对象。他可以是任何我们需要的内容,比如数字,字符串等。在我们的示例中,我们只需要一个数字来表示用户点击的次数,因此将 0 作为变量的初始状态。(如果我们想在状态中存储两个不同的值,我们将调用 seState()两次。)

useState返回的是什么? 它返回一对值:当前 state(状态) 和更新它的函数。 这就是我们编写 const [count, setCount] = useState() 的原因。 这与类中的 this.state.countthis.setState 类似,只不过它们是成对的。 如果您不熟悉我们使用的语法,我们将 在本页底部回到着重说明

现在我们知道了 useState Hook 的作用,我们的例子应该更有说明意义:

import { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

我们声明一个名为 count 的 state(状态)变量,并将其设置为 0 。React 将记住它在重新渲染之间的当前值,并为我们的函数提供最新的值。如果我们想要更新当前 count ,我们可以调用 setCount

注意

您可能想知道:为什么 useState 没有命名为 createState

“Create” 不是非常准确,因为 state(状态) 仅在我们组件第一次渲染时创建。 在下一次渲染期间,useState 为我们提供了当前 state(状态) 。 否则它根本不会是 “state(状态)” ! Hook 名称总是以 use 开头也是有原因的。我们将在后面的 Hooks 规则 中了解。

读取 state(状态)

当我们想要在类中显示当前计数时,我们使用 this.state.count 读取:

  <p>You clicked {this.state.count} times</p>

在函数中,我们直接使用 count 读取:

  <p>You clicked {count} times</p>

更新 state(状态)

在类中,我们需要调用 this.setState() 来更新 count 状态:

  <button onClick={() => this.setState({ count: this.state.count + 1 })}>
    Click me
  </button>

在函数中,我们已经将 setCountcount 作为变量,因此我们不需要 this

  <button onClick={() => setCount(count + 1)}>
    Click me
  </button>

概括

现在让我们用一行一行地回顾一下我们所学到的知识,并检查我们是否已经理解。

 1:  import { useState } from 'react';
 2: 
 3:  function Example() {
 4:    const [count, setCount] = useState(0);
 5:
 6:    return (
 7:      <div>
 8:        <p>You clicked {count} times</p>
 9:        <button onClick={() => setCount(count + 1)}>
10:         Click me
11:        </button>
12:      </div>
13:    );
14:  }
  • 第1行 :我们从 React import(导入) useState Hook。 它允许我们在一个函数组件中保持本地state(状态)。
  • 第4行 :在 Example 组件中,我们通过调用 useState Hook来声明一个新的 state(状态) 变量。它返回一对值,我们给它们命名。我们调用变量 count ,因为它包含按钮单击次数。我们通过传递 0 作为惟一的 useState 参数,将其初始化为 0。它允许我们更新计数,因此我们将其命名为 setCount
  • 第9行:当用户点击时,我们使用新值调用 setCount 。 然后,React 将重新渲染 Example 组件,并将新 count 值传递给它。

乍一看,似乎有很多东西需要考虑。别着急! 如果你对上面的解释中有困惑,再看一遍上面的代码,试着从头到尾读一遍。我们保证,一旦您试图“忘记” state(状态) 如何在 class(类) 中工作,并以全新的眼光看待这段代码,它就会有意义。

提示:方括号意味着什么?

当我们声明一个 state(状态) 变量时,您可能已经注意到方括号:

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

左侧的名称不是 React API 的一部分。您可以根据自己的需求 命名自己的 state(状态) 变量:

  const [fruit, setFruit] = useState('banana');

方括号语法其实是 JavaScript “数组解构” 语法。这意味着我们正在创建两个新变量 fruitsetFruit ,其中 fruit 设置为 useState 是第一个返回值,setFruit 是第二个返回值。它相当于下面这段代码:

  var fruitStateVariable = useState('banana'); // Returns a pair
  var fruit = fruitStateVariable[0]; // First item in a pair
  var setFruit = fruitStateVariable[1]; // Second item in a pair

当我们使用 useState 声明一个 state(状态) 变量时,它返回一个对(pair) - 一个包含两个项元素的数组。第一项元素是当前值,第二项元素是允许我们更新第一项元素值的函数。使用 [0][1] 访问它们有点令人困惑,因为它们有特定的含义。这就是为什么我们使用数组解构的原因。

注意

您可能很好奇,React 是如何知道哪个组件对应哪个 useState,因为我们没有将相关的任何内容传递给React。 我们将在FAQ部分回答 这个问题 和许多其他问题。

提示:使用多个 state(状态) 变量

将 state(状态) 变量 声明为一对 [something, setSomething] 也很方便,因为如果我们想使用多个状态变量,它可以为 不同 的 state(状态) 变量赋予不同的名称:

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

在上面的组件中,我们将 agefruittodos 作为局部变量,我们可以单独更新它们:

  function handleOrangeClick() {
    // Similar to this.setState({ fruit: 'orange' })
    setFruit('orange');
  }

您不必使用很多 state(状态) 变量。 状态变量可以很好地保存对象和数组,因此您仍然可以将相关数据组合在一起。 但是,与 class(类) 中的 this.setState 不同,更新状态变量总是替换它,而不是合并它。

我们 在FAQ中 提供了有关拆分状态变量的更多建议。

下一步

在这个页面上,我们了解了 React 提供的一个名为 useState 的 Hook 。 我们有时也会将其称为 “State(状态) Hook” 。 它允许我们将本地 state(状态) 添加到 React 函数式组件 - 这是我们第一次做到了!

我们还了解了 Hooks 的更多内容。 Hooks 是让您的函数式组件 “接入” React 功能的函数。 他们的名字总是以 use 开头,还有更多我们还没见过的 Hooks 。

现在让我们继续 学习下一个 Hook :useEffect。 它允许您在组件中执行副作用(side effects),类似于类中的生命周期方法。