How to useState in React【译】

449 阅读4分钟

原文地址www.robinwieruch.de/react-usest…

React Hooks 发布赋予了函数组件可以使用状态和副作用的能力。 在众多Hooks中,React 中有两个用于现代状态管理的Hooks:useState 和 useReducer。 本教程逐步介绍 React 中的 useState , 并通过大量示例来帮助您开始使用 React Hook 进行状态管理。

SIMPLE STATE IN REACT

在过去,函数组件中不能使用状态。 因此,它们称为无状态组件。 然而,随着 React Hooks 的发布,状态也可以应用在这类组件当中,因此它们被 React 社区重新命名为函数组件。 以下代码演示了如何在具有 useState 钩子的函数组件中使用状态的简单示例:

const App = () => {
  const [count, setCount] = React.useState(0);
 
  const handleIncrease = () => {
    setCount(count + 1);
  };
 
  const handleDecrease = () => {
    setCount(count - 1);
  };
 
  return (
    <div>
      Count: {count}
      <hr />
      <div>
        <button type="button" onClick={handleIncrease}>
          Increase
        </button>
        <button type="button" onClick={handleDecrease}>
          Decrease
        </button>
      </div>
    </div>
  );
};

这是一个简单的计数demo, useState 函数将初始状态的值作为参数。 在这种情况下,计数从 0 开始。此外,钩子返回一个包含两个值的数组:count 和 setCount。 它们是从允许重命名的返回数组中解构的。

第一个值是count,表示当前值。第二个值是一个函数,用于在调用它时使用传递给该函数的任何内容更新状态。 该函数也称为状态更新函数。 每次调用此函数时,React 都会重新渲染组件以渲染最近的状态。

这就是开始在 React 中进行简单状态管理所需的一切。 如果您对 React 的 useState 的一些使用注意事项感兴趣,请继续阅读。

COMPLEX STATE IN REACT

到目前为止,该示例仅显示了带有 JavaScript 原始类型的 useState。 这就是 useState 闪耀的地方。 它可用于整数、布尔值、字符串和数组。 但是,一旦您计划使用对象或更复杂的数组来管理更复杂的状态,您应该查看 React 的 useReducer 钩子。 useReducer 优于 useState 的场景有很多:

  • 复杂状态容器
  • 复杂的状态转换
  • 有条件的状态更新 它还有助于通过仅使用 useState 来避免多次连续的状态更新。 如果你想在 React 中管理更复杂的状态,你绝对应该尝试使用 useReducer。

ASYNCHRONOUS STATE IN REACT

如果您依赖于实际状态来更新状态,会发生什么? 让我们通过一个例子来说明这种情况,我们使用 JavaScript 内置的 setTimeout 函数延迟状态更新:

const App = () => {
  const [count, setCount] = React.useState(0);
 
  const handleIncrease = () => {
    setTimeout(() => setCount(count + 1), 1000);
  };
 
  const handleDecrease = () => {
    setTimeout(() => setCount(count - 1), 1000);
  };
 
  return (
    <div>
      Count: {count}
      <hr />
      <div>
        <button type="button" onClick={handleIncrease}>
          Increase
        </button>
        <button type="button" onClick={handleDecrease}>
          Decrease
        </button>
      </div>
    </div>
  );
};

每次单击其中一个按钮时,都会延迟一秒调用状态更新函数。 这适用于单击。 但是,请尝试连续多次单击其中一个按钮。 状态更新函数将始终在这一秒内在相同的状态(此处:计数)上运行。 为了解决这个问题,你可以从 useState 传递一个函数给状态更新函数以获取最新变更的状态:

import React from 'react';
 
const App = () => {
  const [count, setCount] = React.useState(0);
 
  const handleIncrease = () => {
    setTimeout(() => setCount(state => state + 1), 1000);
  };
 
  const handleDecrease = () => {
    setTimeout(() => setCount(state => state - 1), 1000);
  };
 
  return (
    <div>
      Count: {count}
      <hr />
      <div>
        <button type="button" onClick={handleIncrease}>
          Increase
        </button>
        <button type="button" onClick={handleDecrease}>
          Decrease
        </button>
      </div>
    </div>
  );
};
 
export default App;

该函数为您提供执行该函数时的状态。 这样,您永远不会在任何陈旧状态下进行操作。 因此,一个好的经验法则可能是:如果您的状态更新取决于您之前的状态,则始终使用 useState 的更新函数中的函数。

React 的 useState 是管理状态的首选钩子。 它可以与 useReducer 和 useContext 一起用于 React 中的现代状态管理。 与 useReducer 相比,它是更轻量级的状态管理方法。

After reading [Translator's Note]

useState赋予初始值

如果是原始类型的值,那么直接赋值,如果是引用类型的值,存在一些差异,数组和对象直接赋值,但是函数需要执行,如下代码age为{name:'name'},name为undefined:

const App = () => {
  const bar = () => {
      return {name:'name'}
  }
  const foo = () => {
      console.log('render');
  }
  const [age, setAge] = useState(bar);
  const [name, setName] = useState(asd);

  return (
    <div>
      Name: {name}
    </div>
  );
};

export default App

useState对于非函数的声明类型,赋初值是一个同步操作,那么如果赋初值的函数是一个异步操作那么结果会如何呢?看下面的代码:

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

const App = () => {
  const bar = async () => {
     const res = await fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json')
     console.log(5,res)
     return res
  }
  const foo = () => {
      console.log(2,'render');
  }
  const [age, setAge] = useState(bar);
  console.log(1,age)
  const [name, setName] = useState(foo);

  console.log(3,age)

  useEffect(() => {
    console.log(4,age)
  })

  return (
    <div>
      Name: {name}
    </div>
  );
};

export default App

其结果是:

1 Promise {<pending>}
index.jsx:14 2 "render"
index.jsx:20 3 Promise {<pending>}
index.jsx:23 4 Promise {<pending>}
index.jsx:10 5 Response {type: "cors", url: "https://gw.alipayobjects.com/os/antvdemo/assets/data/relations.json", redirected: false, status: 200, ok: true, …}

日志按照顺序执行,直接赋给的是Promise {}对象,而不是异步后的结果,说明没有阻塞js执行和UI渲染。