看看应用程序。有一个按钮列表。我真的很难向你们解释这个问题,所以你自己看看这个应用程序,把这当作一个挑战。这确实是一个小应用程序,用于演示我在使用 react 时遇到的问题。这不是基本的东西...
https://codesandbox.io/s/cranky-faraday-bxufk?file=/src/App.js
当点击按钮时,它旁边的布尔值预计会变为 false。乍一看,这对您来说似乎很基础,但是当您查看代码时,这并不容易实现。我的实际应用程序更复杂,所以我制作了一个简单的版本来演示这个问题
Expect:
Click on button 1 => boolean next to it turns to false
Click on button 2 => boolean next to it turns to false
Click on button 3 => boolean next to it turns to false
Reality:
Click on button 1 => screen goes blank
答案 0 :(得分:0)
您代码中的问题与状态更新有关。手柄按钮需要处理状态为:
const handleButton = (id) => {
console.log("On button click: ", id);
setState((state) => {
// Update isSaved of a corresponding button to false
const updatedState = [...state];
const updatedIndex = updatedState.findIndex((item) => item.id === id);
updatedState[updatedIndex] = {
...updatedState[updatedIndex],
isSaved: false
};
return updatedState;
});
};
解决方案实际上是在 setState
变体中,它使用 callback
之类的 setState((latestState) => {// do something })
,并保证每次执行都提供最新的 state
。
感谢您提供一个美丽的问题来解决。
答案 1 :(得分:0)
所以,这是解决这个问题的另一种方法。
但首先我会找出现有代码中的问题,并尝试解释为什么它不起作用。
我在 console.log
中放置了两个 App.js
,一个在 useEffect()
中,另一个在 return
语句之前:
export default function App() {
const [state, setState] = React.useState([]);
// Set pannels state
React.useEffect(() => {
console.log("useEffect executing");
setState(
data.map((item) => ({
id: item.id,
isSaved: true,
content: <Button id={item.id} handleButton={handleButton} />
}))
);
}, []);
const handleButton = (id) => {
....
....
}
console.log("App Rendering");
return (
<div className="App">
<button onClick={handleClick}>Try this</button>
{state ? <Tab pannels={state} /> : null}
</div>
);
}
以下是执行后的控制台:
现在可以清楚地看到,当您在 setState
内执行 useEffect
时,state
值是一个空数组 []。因为 App
的第一个渲染周期已经完成并且调用 useState
将 state
初始化为空数组。
在 useEffect
中,您将属性 content
分配为:
...
content: <Button id={item.id} handleButton={handleButton} />
...
现在,如果您仔细观察 {handleButton}
是一个绑定到“App”(当然它本身就是一个函数)的第一次渲染的函数,并且 {handleButton}
可以访问 const state
它是父作用域,在'App'的第一个渲染周期中是一个空数组[];
因此,当 {handleButton}
正在执行时,您正在将 state
推送到 useEffect
中,该 setState
仍然具有值 [](空数组)。
一旦 useEffect
在 App
内完成,App
的另一个渲染周期就会执行(由于本地状态更新,react 会重新渲染组件)。将创建 state
的新实例及其所有嵌套成员,但由 setState
管理的 react
和 App
除外。
在 state
的第二次渲染之后,您的 {handleButton}
现在包含 content
的旧实例的引用(您将属性 Button
分配给 App
) {handleButton}
的先前渲染因此这些 state
仍然具有 Button
值作为 [](空数组闭包)。
现在,当您单击任何 onClick
时,它的 handleButton
绑定到 state
的旧引用,其中 {handleButton}
值为空数组,因此当 {{ 1}} 完成执行它调用 setState
(由反应框架提供的函数,不受重新渲染的影响)它再次将 state
设置为空数组。你最终让你的按钮消失了。
现在要解决这个问题,您需要在每次为 {handleButton}
重新渲染时重新绑定 App
的新实例。您无法记住 {handleButton}
,因为它取决于 state
。因此,您可以将 Button
组件的渲染委托给 Tab
,后者在每次 state
更新后重新渲染。
所以你可以这样做:
App.js
export default function App() {
const [state, setState] = React.useState([]);
// Set pannels state
React.useEffect(() => {
setState(
data.map((item) => ({
id: item.id,
isSaved: true
}))
);
}, []);
const handleButton = (id) => {
console.log("On button click: ", id);
console.log(state);
//Update isSaved of a corresponding button to false
const updatedState = [...state];
const updatedIndex = updatedState.findIndex((item) => item.id === id);
updatedState[updatedIndex] = {
...updatedState[updatedIndex],
isSaved: false
};
setState(updatedState);
};
return (
<div className="App">
<button onClick={() => handleButton(2)}>Try this</button>
{state ? <Tab pannels={state} handleButton={handleButton} /> : null}
</div>
);
}
注意:我什至还为 handleButton
按钮使用了相同的事件处理程序 try this
,以表明如果在组件本身上使用或传递给子组件,相同的函数实例也能正常工作。
Tab.js
const Tab = ({ pannels, handleButton }) => {
return (
<div>
List of pannels
{pannels.map((item) => (
<div key={item.id}>
<Button id={item.id} handleButton={handleButton} />
<span>{item.isSaved ? "true" : "false"}</span>
</div>
))}
</div>
);
};
代码沙盒参考:https://codesandbox.io/s/zen-pascal-o6ify
谢谢