React useState() 使用指南

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React useState() 使用指南相关的知识,希望对你有一定的参考价值。

参考技术A useState()是改变状态的开关,将状态添加到函数组件需要4个步骤:启用状态、初始化、读取和更新。

要将<Bulbs> 转换为有状态组件,需要告诉 React:从'react'包中导入useState钩子,然后在组件函数的顶部调用useState()。

在Bulbs函数的第一行调用useState(),在组件内部调用会使该函数成为有状态的函数组件。
启用状态后,下一步是初始化。

useState(false)用false初始化状态。

on状态变量保存状态值。

状态已经启用并初始化,现在可以读取它了。但是如何更新呢?再来看看useState(initialState)返回什么。

useState(initialState)返回一个数组,其中第一项是状态值,第二项是一个更新状态的函数。

状态一旦改变,React 就会重新渲染组件,on变量获取新的状态值。
状态更新作为对提供一些新信息的事件的响应。这些事件包括按钮单击、HTTP 请求完成等,确保在事件回调或其他回调中调用状态更新函数。

setOn(on => !on)使用函数更新状态。

通过多次调用useState(),一个函数组件可以拥有多个状态。

[on, setOn] = useState(false) 管理开/关状态
[count, setCount] = useState(1)管理数量。
多个状态可以在一个组件中正确工作

每当 React 重新渲染组件时,都会执行useState(initialState)。 如果初始状态是原始值(数字,布尔值等),则不会有性能问题。
当初始状态需要昂贵的性能方面的操作时,可以通过为useState(computeInitialState)提供一个函数来使用状态的延迟初始化,如下所示:

getInitialState()仅在初始渲染时执行一次,以获得初始状态。在以后的组件渲染中,不会再调用getInitialState(),从而跳过昂贵的操作。

在使用useState() 时,必须遵循的规则
1、仅顶层调用:不能在循环,条件,嵌套函数等中调用useState().在多个useState()调用中,渲染之间的调用顺序必须相同。
2、仅从React 函数调用 :必须仅在函数组件或自定义钩子内部调用useState()。
下面看下useState()的正确用法和错误用法的例子。

useState()在函数组件的顶层被正确调用

以相同的顺序正确地调用多个useState()调用:

useState()在自定义钩子的顶层被正确调用

在条件中调用useState()是不正确的

在嵌套函数中调用useState()也是不对的

闭包是一个从外部作用域捕获变量的函数。
闭包(例如事件处理程序,回调)可能会从函数组件作用域中捕获状态变量。 由于状态变量在渲染之间变化,因此闭包应捕获具有最新状态值的变量。否则,如果闭包捕获了过时的状态值,则可能会遇到过时的状态问题。
来看看一个过时的状态是如何表现出来的。组件<DelayedCount>延迟3秒计数按钮点击的次数

快速多次点击按钮。count 变量不能正确记录实际点击次数,有些点击被吃掉。
delay() 是一个过时的闭包,它从初始渲染(使用0初始化时)中捕获了过时的count变量。
为了解决这个问题,使用函数方法来更新count状态:

现在setCount(count => count + 1)在delay()中正确更新计数状态。React 确保将最新状态值作为参数提供给更新状态函数,过时闭包的问题解决了。
快速单击按钮。 延迟过去后,count 能正确表示点击次数。

useState()用于管理简单状态。对于复杂的状态管理,可以使用useReducer() 。它为需要多个状态操作的状态提供了更好的支持。
假设需要编写一个最喜欢的电影列表。用户可以添加电影,也可以删除已有的电影,实现方式大致如下:

状态列表需要几个操作:添加和删除电影,状态管理细节使组件混乱。
更好的解决方案是将复杂的状态管理提取到reducer中:
reducer 接受两个参数一个是 state 另一个是 action 。然后返回一个状态 count 和 dispath,count 是返回状态中的值,而 dispatch 是一个可以发布事件来更新 state 的
在 useReducer 传入 reducer 函数根据 action 来更新 state,如果 action 为 add 正增加 state 也就是增加 count。
在 button 中调用 dispatch 发布 add 事件,发布 add 事件后就会在 reducer 根据其类型对 state 进行对应操作,更新 state。

reducer 管理电影的状态,有两种操作类型:

注意组件功能没有改变。但是这个版本的 <FavoriteMovies> 更容易理解,因为状态管理已经被提取到 reducer 中。

还有一个好处:可以将 reducer 提取到一个单独的模块中,并在其他组件中重用它。另外,即使没有组件,也可以对 reducer 进行单元测试。
这就是关注点分离的威力:组件渲染UI并响应事件,而 reducer 执行状态操作。
查看效果: https://codesandbox.io/s/react-usestate-complex-state-usereducer-gpw87

考虑这样一个场景:咱们想要计算组件渲染的次数。
一种简单的实现方法是初始化countRender状态,并在每次渲染时更新它(使用useEffect())
函数组件中没有生命周期,那么可以使用 useEffect 来替代。如果你熟悉 React class 的生命周期函数,你可以把 useEffect 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

useEffect()在每次渲染后调用afterRender()回调。但是一旦countRender状态更新,组件就会重新渲染。这将触发另一个状态更新和另一个重新渲染,依此类推。
可变引用useRef()保存可变数据,这些数据在更改时不会触发重新渲染,使用可变的引用改造一下<CountMyRenders> :

每次渲染组件时,countRenderRef可变引用的值都会使countRenderRef.current ++递增。 重要的是,更改不会触发组件重新渲染。
打开例子: https://codesandbox.io/s/react-usestate-vs-useref-g6qv3?file=/src/index.js

要使函数组件有状态,请在组件的函数体中调用useState()。
useState(initialState)的第一个参数是初始状态。返回的数组有两项:当前状态和状态更新函数。

使用 setState(newState)来更新状态值。 另外,如果需要根据先前的状态更新状态,可以使用回调函数setState(prevState => newState)。

在单个组件中可以有多个状态:调用多次useState()。

当初始状态开销很大时,延迟初始化很方便。使用计算初始状态的回调调用useState(computeInitialState),并且此回调仅在初始渲染时执行一次。

必须确保使用useState()遵循规则。

当闭包捕获过时的状态变量时,就会出现过时状态的问题。可以通过使用一个回调来更新状态来解决这个问题,这个回调会根据先前的状态来计算新的状态。

使用useState()来管理一个简单的状态。为了处理更复杂的状态,一个更好的的选择是使用useReducer() 。

以上是关于React useState() 使用指南的主要内容,如果未能解决你的问题,请参考以下文章

React useState() 使用指南

[react] 请说说什么是useState?为什么要使用useState?

无法在类组件 React.js 中使用 useState

使用 useState 在 React 组件之间传递值

React useState:如何使用 onChange 输入和 OnClick 按钮更新我的 useState?

react的hook踩坑,useState的set方法不生效问题。