在 React 中,状态管理是构建组件的重要组成部分,
useState
Hook 提供了一种简单而灵活的方式来管理函数组件中的状态。大多数开发者都知道,调用
useState
可以创建一个状态变量和一个更新该状态的函数,但在实际开发中,很多人对多次调用
useState
会触发多次更新的行为感到困惑。本文将深入探讨
useState
的工作原理,何时触发更新,如何批量更新状态,以及在使用
useState
时应遵循的最佳实践。
一、什么是 useState?
在 React 中,
useState
是一个 Hook,用于在函数组件中添加状态。使用
useState
可以在组件中创建一个状态变量,并返回一个数组,其中包含当前状态和一个更新该状态的函数。
1. 基本用法
useState
的基本用法如下:
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0); // 初始化状态为 0
const increment = () => {
setCount(count + 1); // 更新状态
return (
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
export default Counter;
在这个示例中,
useState
创建了一个名为
count
的状态变量,并通过
setCount
函数更新它。每次点击按钮时,
count
的值会增加 1,组件会根据新值重新渲染。
二、useState 的更新机制
1. 状态更新的异步性
在 React 中,状态更新是异步的。这意味着,调用
setState
(在这里是
setCount
)后,React 不会立即更新状态,而是将其标记为需要更新,并在适当的时候重新渲染组件。这种异步更新机制使得 React 能够优化性能,减少不必要的渲染。
2. 触发更新的条件
调用
setState
将触发组件的重新渲染。每次调用
setCount
都会导致组件使用更新后的状态重新渲染。以下是一些触发更新的情况:
-
直接调用
setCount
:每次调用
setCount
都会触发更新。
-
多个状态更新
:在同一事件处理函数内,可以多次调用
setCount
,但 React 会将这些更新合并到一次渲染中。
例如:
const incrementTwice = () => {
setCount(count + 1);
setCount(count + 1); // 两次调用
在这个示例中,虽然
setCount
被调用了两次,React 只会触发一次渲染,并且
count
只会加 1。
3. 使用函数式更新
为了避免由于闭包造成的状态更新问题,可以使用函数式更新来确保获取到最新的状态值。这种方式更为安全,特别是在多次更新状态时。
const incrementTwice = () => {
setCount((prevCount) => prevCount + 1); // 使用前一个状态
setCount((prevCount) => prevCount + 1); // 使用前一个状态
在这个示例中,
prevCount
是最新的状态值,确保每次增量都基于最新值。
三、批量更新状态
1. React 的批量更新机制
在 React 中,多个状态更新可能会合并为一次渲染。这适用于事件处理函数、生命周期方法和其他 React 事件中。React 会自动批量处理这些更新,以提高性能。
const handleClick = () => {
setCount(count + 1); // 第一次更新
setCount(count + 1); // 第二次更新
setCount(count + 1); // 第三次更新
在这个例子中,虽然调用了三次
setCount
,但 React 会将这三次更新合并为一次渲染。最终,组件的
count
值只会增加 1,而不是 3。
2. 异步更新与批量更新
需要注意的是,批量更新仅适用于 React 的特定情况。如果状态更新是在 setTimeout、Promise 等异步回调中,React 不会自动批量处理这些更新。
const handleAsyncClick = () => {
setTimeout(() => {
setCount(count + 1); // 异步更新,React 不会批量处理
setCount(count + 1); // 再次异步更新
}, 1000);
在这个例子中,由于状态更新是在
setTimeout
中进行的,React 将不会批量处理这些更新。最终,
count
的值将增加 2 而不是 1。
四、useState 的最佳实践
1. 使用函数式更新
在需要多次更新状态时,使用函数式更新的方式可以确保获取到最新的状态,避免因闭包造成的错误。例如:
const increment = () => {
setCount((prevCount) => prevCount + 1);
setCount((prevCount) => prevCount + 1);
2. 避免直接使用状态值
在调用
setCount
时,避免直接使用状态值。因为状态更新是异步的,直接使用状态值可能导致不正确的渲染结果。总是使用函数式更新确保获取最新状态。
const increment = () => {
setCount(count + 1); // 不推荐
setCount(count + 1); // 不推荐
// 推荐使用
const incrementSafe = () => {
setCount((prevCount) => prevCount + 1);
setCount((prevCount) => prevCount + 1);
3. 考虑性能
在编写事件处理逻辑时,尤其是涉及到大量状态更新时,考虑性能是非常重要的。使用批量更新和函数式更新可以帮助提高性能,使应用更加高效。
4. 适当使用 useReducer
如果组件的状态逻辑复杂,可能涉及多个子值或依赖于先前的状态,考虑使用
useReducer
来管理状态,这能够提供更清晰的状态管理和更新逻辑。
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
export default Counter;
在 React 中,
useState
提供了一种简便的方式来管理组件状态。通过正确理解
useState
的更新机制,特别是多次调用
setState
的行为,开发者可以避免一些常见的错误,并提高应用的性能和可维护性。
在使用
useState
时,始终关注状态更新的异步性和批量更新的特性,并使用函数式更新来确保访问最新的状态。这将帮助你编写出更加高效和稳定的 React 组件。通过遵循最佳实践,你可以更好地利用
useState
管理组件状态,从而提升用户体验和代码的清晰度。希望通过本文的讲解,能够帮助你更深入地理解
useState
的使用,从而在实际项目中灵活运用。
今天我遇到的是 tab选项卡里放FlatList长列表,发现每次上拉加载更多的时候onEndReached会
执行
两次,分页数据每次请求2页,数据也会抖动一下,这肯定不是我想要的。
然后百度、google了一下,哟西,原来很多人遇到,貌似是官方bug,具体怎么产生的自行百度或者看官方issues
我也找了很多别人的方法,复制粘贴梭哈一试,NM有个鸟用,闭着眼睛估计不知道从哪梭哈过来的
不过万幸的是给提供了四个新方法
循环
调用
useState
同步处理方案(添加数据)
1. 回调函数
useState
(data) 改成:
useState
(data => 处理data)
2. 抽离函数外使用变量
循环
调用
usesate内,再调接口添加到原数据方案(添加数据+异步接口)
1. 可改造 setArr为function
2. 封装使用
useState
的回调函数
3. 借用useEffect监听变化
推荐阅读:react循环遍历
useState
的数组异步调取接口追加参数后修改原数组处理方案:https:/
方案01. 可改造 setArr为function
方案02. 封装使用
useState
的回调函数
方案03:【推荐】useEffect + es6中的Promise.all()处理(复杂任务也适用)
方案04:useEffect + es5中的回调函数callback处理(逻辑有点绕,因要固定排序)
方案05:useEffect +
useState
回调函数写法(数据量大的时候需要额外测试处理)
1.可改造 setArr为funct