无法理解 Counter 示例中的 React 陈旧状态
Trouble understanding React stale state in Counter example
我确定我的问题是关于 useCallback 和 setter 方法,我在这里 setSpeakers
从 useState
返回,但我不明白为什么我的例子,如所列here 和 codesandbox 中的状态没有正确递增。旧状态不断返回,而不是新的递增状态。当我将一个函数传递给 setSpeakers
而不仅仅是一个新状态时,我的示例有效(您可以在非工作代码下方看到标记为 THIS CODE WORKS 的注释掉的代码。
我知道其他人已经写过这方面的文章,我已经阅读了那些文章,但仍然不明白。
沙盒:https://codesandbox.io/s/elated-lichterman-7rejs?file=/pages/index.js
import React, { useState, memo, useCallback } from "react";
//const Speaker = React.memo(({ imageSrc, counter, setCounter }) => {
const Speaker = ({ speaker, speakerClick }) => {
console.log(speaker.id);
return (
<div>
<span
onClick={() => {
speakerClick(speaker.id);
}}
src={`speakerimages/Speaker-${speaker.id}.jpg`}
width={100}
>
{speaker.id} {speaker.name}
</span>
<span className="fa fa-star "> {speaker.clickCount}</span>
</div>
);
};
function SpeakerList({ speakers, setSpeakers }) {
return (
<div>
{speakers.map((speaker) => {
return (
<Speaker
speaker={speaker}
speakerClick={useCallback((id) => {
// THIS CODE FAILS BECAUSE OF STALE STATE BUT I DON'T GET WHY
const speakersNew = speakers.map((speaker) => {
return speaker.id === id
? { ...speaker, clickCount: speaker.clickCount + 1 }
: speaker;
});
setSpeakers(speakersNew);
// THIS CODE WORKS
// setSpeakers(function(speakers) {
// const speakersNew = speakers.map((speaker) => {
// return speaker.id === id
// ? { ...speaker, clickCount: speaker.clickCount + 1 }
// : speaker;
// });
// return speakersNew;
// });
}, [])}
key={speaker.id}
/>
);
})}
</div>
);
}
//
const App = () => {
const speakersArray = [
{ id: 1124, name: "aaa", clickCount: 0 },
{ id: 1530, name: "bbb", clickCount: 0 },
{ id: 10803, name: "ccc", clickCount: 0 }
];
const [speakers, setSpeakers] = useState(speakersArray);
return (
<div>
<h1>Speaker List</h1>
<SpeakerList speakers={speakers} setSpeakers={setSpeakers}></SpeakerList>
</div>
);
};
export default App;
那是因为设置状态是异步工作的。在第一个不是 运行 的代码中,您设置状态并尝试立即显示它不会工作,因为在设置状态时存在延迟。第二个之所以有效,是因为您将回调传递给设置状态,这基本上意味着设置状态并在您处于状态时执行此操作。而第一个意味着设置状态然后再执行此操作。那不是你想要的。这是一个松散的类比,但看看 set state 是如何以及为什么是 aync 的,你会有一个更好的主意。干杯。
我确定我的问题是关于 useCallback 和 setter 方法,我在这里 setSpeakers
从 useState
返回,但我不明白为什么我的例子,如所列here 和 codesandbox 中的状态没有正确递增。旧状态不断返回,而不是新的递增状态。当我将一个函数传递给 setSpeakers
而不仅仅是一个新状态时,我的示例有效(您可以在非工作代码下方看到标记为 THIS CODE WORKS 的注释掉的代码。
我知道其他人已经写过这方面的文章,我已经阅读了那些文章,但仍然不明白。
沙盒:https://codesandbox.io/s/elated-lichterman-7rejs?file=/pages/index.js
import React, { useState, memo, useCallback } from "react";
//const Speaker = React.memo(({ imageSrc, counter, setCounter }) => {
const Speaker = ({ speaker, speakerClick }) => {
console.log(speaker.id);
return (
<div>
<span
onClick={() => {
speakerClick(speaker.id);
}}
src={`speakerimages/Speaker-${speaker.id}.jpg`}
width={100}
>
{speaker.id} {speaker.name}
</span>
<span className="fa fa-star "> {speaker.clickCount}</span>
</div>
);
};
function SpeakerList({ speakers, setSpeakers }) {
return (
<div>
{speakers.map((speaker) => {
return (
<Speaker
speaker={speaker}
speakerClick={useCallback((id) => {
// THIS CODE FAILS BECAUSE OF STALE STATE BUT I DON'T GET WHY
const speakersNew = speakers.map((speaker) => {
return speaker.id === id
? { ...speaker, clickCount: speaker.clickCount + 1 }
: speaker;
});
setSpeakers(speakersNew);
// THIS CODE WORKS
// setSpeakers(function(speakers) {
// const speakersNew = speakers.map((speaker) => {
// return speaker.id === id
// ? { ...speaker, clickCount: speaker.clickCount + 1 }
// : speaker;
// });
// return speakersNew;
// });
}, [])}
key={speaker.id}
/>
);
})}
</div>
);
}
//
const App = () => {
const speakersArray = [
{ id: 1124, name: "aaa", clickCount: 0 },
{ id: 1530, name: "bbb", clickCount: 0 },
{ id: 10803, name: "ccc", clickCount: 0 }
];
const [speakers, setSpeakers] = useState(speakersArray);
return (
<div>
<h1>Speaker List</h1>
<SpeakerList speakers={speakers} setSpeakers={setSpeakers}></SpeakerList>
</div>
);
};
export default App;
那是因为设置状态是异步工作的。在第一个不是 运行 的代码中,您设置状态并尝试立即显示它不会工作,因为在设置状态时存在延迟。第二个之所以有效,是因为您将回调传递给设置状态,这基本上意味着设置状态并在您处于状态时执行此操作。而第一个意味着设置状态然后再执行此操作。那不是你想要的。这是一个松散的类比,但看看 set state 是如何以及为什么是 aync 的,你会有一个更好的主意。干杯。