预测下刷新页面后,页面上会有啥数字出现
import React, { useState, useEffect } from "react";const Bpp = () => {const [data, setData] = useState([]);// const [loading, setLoading] = useState(true);const [errorData, setErrorData] = useState([]);useEffect(() => {const newList = [];for (let i = 0; i < 1000; i++) {newList.push({name: `标题${i}`,});}setData(newList);setErrorData(data);}, []);return (<div>{data.length},{errorData.length}</div>)
}export default Bpp;
一、组件的初始化阶段:
-
状态初始化:
const [data, setData] = useState([]);
:将data
初始化为一个空数组。const [errorData, setErrorData] = useState([]);
:将errorData
初始化为一个空数组。
-
首次渲染:
- 当组件首次渲染时,会执行
return
语句,显示<div>{data.length},{errorData.length}</div>
。 - 由于
data
和errorData
都是初始状态,它们的长度都是0
,所以页面会显示0,0
。
- 当组件首次渲染时,会执行
二、useEffect
的执行:
-
useEffect
触发:- 由于
useEffect
的依赖项数组为[]
,该useEffect
只会在组件挂载后执行一次,类似于componentDidMount
生命周期方法。 - 当组件挂载完成后,
useEffect
开始执行。
- 由于
-
useEffect
内部操作:- 一个包含 1000 个元素的
newList
被创建:const newList = []; for (let i = 0; i < 1000; i++) {newList.push({name: `标题${i}`,}); }
setData(newList);
被调用:- 这将触发 React 状态更新,把
data
状态更新为包含 1000 个元素的数组。但 React 的状态更新是异步的,意味着data
的更新不会立即完成。React 不会立即更新data
的值,而是会将状态更新排入队列,并在稍后进行更新
- 这将触发 React 状态更新,把
setErrorData(data);
被调用:- 由于状态更新的异步性,此时
data
仍然保持其旧值(即空数组),因为setData
操作尚未完成更新。因此,setErrorData
会将errorData
状态更新为一个空数组。
- 由于状态更新的异步性,此时
- 一个包含 1000 个元素的
三、最终结果:
- 后续渲染:
- 在
useEffect
执行之后,组件会重新渲染。 - 此时,
data
状态已经更新,因为setData
的更新操作已经完成,所以data.length
为1000
。 - 但是,
errorData
状态仍然是空数组,因为在useEffect
中,setErrorData
接收到的是data
的旧值,所以errorData.length
为0
。
- 在
- 页面上最终显示
1000,0
的原因是useEffect
内部状态更新的顺序和异步特性。在useEffect
中调用setData(newList)
不会立即更新data
,而后续的setErrorData(data)
使用的是data
的旧值(即初始的空数组),导致errorData
最终仍然是一个空数组,而data
最终更新为包含 1000 个元素的数组。
解决代码
import React, { useState, useEffect } from "react";const Bpp = () => {const [data, setData] = useState([]);// const [loading, setLoading] = useState(true);const [errorData, setErrorData] = useState([]);useEffect(() => {const newList = [];for (let i = 0; i < 1000; i++) {newList.push({name: `标题${i}`,});}setData(newList);}, []);useEffect(() => {setErrorData(data);}, [data]);return (<div>{data.length},{errorData.length}</div>)
}export default Bpp;