gpt4 book ai didi

javascript - 与 useState 的异步性质相比,React 的 useReducer 是同步的吗?

转载 作者:行者123 更新时间:2023-12-04 16:38:58 25 4
gpt4 key购买 nike

我的印象是 useStateuseReducer 的工作方式相似,只是当状态是复杂/嵌套对象时我们应该使用 useReducer。

但是今天我发现了一个奇怪的行为,我正在循环一个数组并将值设置为一个状态对象。我使用 useStateuseReducer 做了同样的例子。

使用useState:它只是将数组中的最后一个值推送到状态对象,因为useState本质上是异步的,所以当我们在循环中设置状态时,它可能无法根据之前的正确更新状态。所以你只得到状态中的最后一个对象。

使用 useReducer:我期待 useReducer 具有相同的行为,但使用 useReducer,当我们从循环内 dispatch 操作时,它似乎可以正确设置状态。所以在这里你可以得到状态中的所有对象。

使用状态

import React from 'react';
import ReactDOM from 'react-dom';

function App() {
const [students, setStudents] = React.useState({});

const createStudents = () => {
const ids = [1,2,3];
const names = ['john', 'michael', 'greg']
for(let i = 0; i < 3; i++){
const student = {[ids[i]]: names[i]};
setStudents({...students, ...student})
}
}
return (
<div className="App">
<button onClick={createStudents}>Create Students</button>
<br />
{JSON.stringify(students)}
</div>
);
}

使用Reducer

import React from 'react';
import ReactDOM from 'react-dom';

function App() {
const studentReducer = (state, action) => {
switch (action.type) {
case 'ADD':
return {...state, students: {...state.students, ...action.payload}};
default:
throw new Error();
}
}

const [students, dispatch] = React.useReducer(studentReducer, {students: {}});

const createStudents = () => {
const ids = [1,2,3];
const names = ['john', 'michael', 'greg']
for(let i = 0; i < 3; i++){
const student = {[ids[i]]: names[i]};
dispatch({type: 'ADD', payload: student})
}
}
return (
<div className="App">
<button onClick={createStudents}>Create Students</button>
<br />
{JSON.stringify(students)}
</div>
);
}

最佳答案

I was in a impression that both useState and useReducer works similarly

这种印象是正确的。如果我记得,useState 甚至 calls useReducer,所以真的 useState 基本上是 useReducer 的一个特例 (我之前在 react 的源代码中查找过这个,但我找不到它来提供链接)。

您看到的行为与它是同步还是异步无关,而与您是根据闭包中的值计算新状态还是根据最新值计算新状态有关。考虑这段代码:

const [students, setStudents] = React.useState({});
//... some code omitted
setStudents({...students, ...student})

注意students是一个const。它永远不会改变,即使你调用 setStudents 也是如此。如果您只设置一次状态,那并不是真正的问题:您将复制学生,添加一个新学生,然后将其传递。然后组件重新呈现。在该新渲染上,将创建一个新的局部变量,其中包含一个学生的新对象。然后,新渲染中的代码可以与该对象与一名学生进行交互,并可能创建一个与两名学生一起使用的对象。

但是如果你在一个循环中执行它,那么每次通过循环你都是从空对象开始的。您复制空对象,添加学生 A,并告诉 react 设置状态。 students 没有改变,因此您再次复制相同的空对象,并添加学生 B。请注意,第二次不包括 A。最终循环结束,唯一的 setStudents最重要的是最后一个。

设置状态时,您可以使用另一种形式。你传入一个函数,react 将使用最新值调用该函数:

setStudents(previous => {
return {
...previous,
...student
}
});

使用这种方法,previous 以空对象开始,然后添加学生 A。然后在下一次循环中,previous 现在是包含学生 A 的对象,然后添加学生 B。所以当大功告成,您将拥有所有学生,而不仅仅是最后一个。


回到你关于 useState 和 useReducer 的问题:它们不同的原因是 useReducer 总是使用回调形式。 reducer 函数将始终以最新状态传递,因此您基于该状态计算新状态,而不是基于任何 students 等于上次呈现组件的时间。如果您重做代码以使用上面所示的回调,您也可以在 useState 中使用相同的东西。

关于javascript - 与 useState 的异步性质相比,React 的 useReducer 是同步的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64538271/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com