使用State Hook

335 阅读9分钟

原文:reactjs.org/docs/hooks-…

Hooks是React16.8版本中新增的特性。他能够让你在不写class的场景中使用state和其他React特性。

介绍页面上使用了这个例子来熟悉Hooks:

import React, { useState } from 'react';

function Example() {
  // 声明一个新的state变量,我们将其称之为“count”
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

我们将开始通过将这个代码与等价的class类组件的例子对比来学习Hooks的相关知识。


等价的Class例子 如果你之前使用过类组件,这个代码将会看起来比较熟悉。

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

state的初始值为{ count: 0 },当用户点击按钮,触发this.setState(),我们会将state.count值+1。我们将在整个页面上从类组件中使用这个片段。

注意
你也许想知道为什么在这里使用一个计数器而不是一个更加复杂的例子。这将有助于让我们在Hooks上起步时,可以专注于API。


Hooks和函数组件 作为一个提醒,在React中的函数组件看起来像是这样:

const Example = (props) => {
  // 你可以在这里使用Hooks!
  return <div />;
}

或者这样:

function Example(props) {
  // 你可以在这里使用Hooks!
  return <div />;
}

你先前也许已经将这些认知为“无状态组件”。我们正将使用React 状态的能力引入到这些中,因此我们会更倾向于将他们命名为“函数组件”。

Hooks在classes中不能工作。但是你可以不是写classes,而去使用他们。


什么是一个Hook?

我们通过从React中引入useStateHook,来开始新的例子。

import React, { useState } from 'react';

function Example() {
  // ...
}

什么是一个Hook?一个Hook是一个非常特殊的函数能够让你“挂住”React的特性。举个例子,useState是一个Hook,能够让你给函数组件添加React的state。我们之后将学习其他的Hooks。

什么时候我会使用一个Hook?如果你写一个函数组件,并且意识到你需要给它添加一些state状态值,先前,你必须将它转化成一个class。现在你可以在现有的函数组件内部使用一个Hook。我们现在就要去做那些啦!

注意
关于哪里在一个组件内部,哪里你可以和哪里不可以使用Hooks,有一些特殊的规则。我们将在 Hooks的规则 中学习。


声明一个State变量

在一个class中,我们在构造函数constructor中通过设置将this.state设置为{count: 0},将count状态值初始值设为0:

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

在一个函数组件中,我们没有this,因此我们不能赋值或者是读取this.state。相反地,我们在我们的组件内部直接调用useStateHook。

import React, { useState } from 'react';

function Example() {
  // 声明一个新的state变量,我们将其称之为"count"
  const [count, setCount] = useState(0);

调用useState用来做什么呢?它声明了一个“状态变量”。我们的变量被称之为count,但我们可以将它称之为其他的名称,例如:banana.这是一种在函数间调用“保存”一些数值的方式-- useState是一种是能够实现this.state在一个class中相同的能力的新的方式。通常地,当函数存在但是state变量会被React保存起来时,变量消失了。

我们将什么作为一个参数传给useState?使用useState()Hook唯一的参数是初始的state值。跟classes不同,state状态值不必一定是一个对象。状态值比较简单的时候,我们可以将它设置为number或者是string类型。在我们的例子中,我们只需要保存一个用户点击了多少次的状态值,因此我们可以传0作为初始的状态值。(如果我们想在state状态值中存储两个不同的值,我们将会调用useState()两次。)

useState返回什么呢?它会返回一对变量值:当前的状态值和用来更新状态值的函数。这就是为什么我们这样编写const [count, setCount] = useState().这跟在一个class中的this.state.countthis.setState 类似,除了你是在一个数组中获取他们。如果你对我们使用的语法不熟悉,我们将会在本文的底部再回过头来进行讲诉。

现在我们已经知道useStateHook做了什么,我们的例子应该有了更多的意义:

import React, { useState } from 'react';

function Example() {
  // 声明一个state变量,我们称之为"count"
  const [count, setCount] = useState(0);

我们声明一个叫做count的state变量,并将它设置为0。React将会在再次渲染之间记住它当前的值,并且给我们的函数提供最近的一个值。如果我们想更新当前count值,我们可以调用setCount.

注意
你可能想知道:为什么useState不命名为createState?
"Create"不是特别的精确,因为state只在我们的组件渲染的第一次会被创建出来。在下一次render期间,useState将会给我们的当前状态值。否则的话它将根本不是“state”!对于为什么Hook一直以use开头还有另外一个原因。我们将在稍后的 Hooks的规则 一文中学习。


读取State

当我们在一个class中想要展现当前的count值,我们读取this.state.count

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

在一个函数中,我们可以直接使用count:

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

更新State

在一个class中,我们需要调用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 React, { 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中导入useStateHook。它让我们在一个函数组件中本地保存state。
  • 第4行:在这个Example组件内部,我们通过调用useStateHook,声明了一个新的state变量。它返回一对值,我们可以给他们命名。我们将变量命名为count,因为它保存的是按钮点击的次数。我们通过将0作为useState的唯一参数传入,将count值初始化为0.第二个返回的是它本身的一个函数。它让我们能够更新count值,因此我们将它命名为setCount
  • 第9行:当用户点击的时候,我们用一个新的值来调用setCount。React随后将会再次渲染Example组件,将新的count值传给它。

这些概念对于刚刚起步来说可能太多了。不要太着急!如果你在解释上迷失了,再次查看下上面的代码,是这从上到下的阅读它。我们确保只要你试着忘记在classes中的state是如何工作的,然后用全新的视角查看这段代码,它将会变得有意义。

Tip:中括号意味着什么?

在我们声明一个state变量的时候,你也许已经注意到中括号:

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

左边的的命名并不是React API中的一部分。你可以命名自己的state变量:

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

这个Javascript语法叫做 "数组解构" 。它意味着我们正在创建两个新的变量fruitsetFruitfruit被设置为useState返回值的第一个值,setFruit是返回值的第二个。它跟下面的这段代码等价:

  var fruitStateVariable = useState('banana'); // 返回一个值
  var fruit = fruitStateVariable[0]; // 数组中的第一个值
  var setFruit = fruitStateVariable[1]; // 数组中的第二个

当我们用useState声明一个state变量的时候,它返回了一对值-- 一个有两个值的数组。第一个值是当前的值,第二个值是一个函数,能够让我更新第一个值。使用[0][1]来获取他们有一点令人疑惑,因为他们有特殊的含义。这就是为什么我们使用数组解构的方式。

注意
你也许会好奇React是怎么知道useState是跟哪一个组件相匹配,因为我们没有回传给React传递任何类似于this的内容。我们将在FAQ章节回答 这个问题 以及其他的问题。

Tip:使用多种State变量

如果我们想要不止一次的使用他们的话,声明类似于[something, setSomething]也是非常有用的,因为它能够让我们给不同的state变量不同的名字:

function ExampleWithManyStates() {
  // 声明多种不同的state变量!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

在上面的组件中,我们有本地的变量agefruittodos,并且我们可以各自的更新他们:

  function handleOrangeClick() {
    // 类似于this.setState({ fruit: 'orange' })
    setFruit('orange');
  }

你不是非得定义非常多的state变量。State变量可以是对象和数组,因为你还可以将数据组装在一起。然而,不同于一个class中的this.setState,更新一个state变量总是会替换它,而不是将它合并。

我们 在FAQ中 提供了更多的关于在独立的state变量上进行分割的建议。


下一步

在本文中,我们已经学习了React提供的Hooks中的一个,叫做useState.我们有时也叫它“State Hook”.它能够让我们将本地的状态值添加到React函数组件中 -- 这是我们第一次做这个!

我们也学到了更多关于Hooks是什么的内容。Hooks是函数,能够让你从函数组件中“勾住”React的特性。他们的名字总是以use开头,还有更多我们还没有见过的Hooks。

现在让我们继续 学习下一个Hook:useEffect。它能够在组件中让你运行副作用,并且它跟classes中的生命周期方法类似。