Reactjs 从子组件更新状态
看看这个应用程序。有一个按钮列表。我很难向你们解释这个问题,所以亲自看看这个应用程序,把它看作是一个挑战。这真的是一个小应用程序,用来演示我在使用react时遇到的问题。这不是一个基本的东西 单击按钮时,它旁边的布尔值将变为false。起初,这似乎很基本,但当您查看代码时,这并不容易实现。我的实际应用程序更复杂,所以我制作了一个简单的版本来演示这个问题Reactjs 从子组件更新状态,reactjs,Reactjs,看看这个应用程序。有一个按钮列表。我很难向你们解释这个问题,所以亲自看看这个应用程序,把它看作是一个挑战。这真的是一个小应用程序,用来演示我在使用react时遇到的问题。这不是一个基本的东西 单击按钮时,它旁边的布尔值将变为false。起初,这似乎很基本,但当您查看代码时,这并不容易实现。我的实际应用程序更复杂,所以我制作了一个简单的版本来演示这个问题 Expect: Click on button 1 => boolean next to it turns to false Click
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
代码中的问题与状态更新有关。handle按钮需要将状态处理为:
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
变量中,它使用回调
如setState((latestState)=>{//do something})
并保证为每次执行提供最新的状态
感谢您提出了一个漂亮的问题来解决。因此,这是解决这个问题的另一种方法
但首先,我将确定现有代码中的问题,并尝试解释为什么它不起作用
我在App.js中放置了两个console.log
,一个在useffect()
中,另一个在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>
);
}
现在,如果您仔细观察{handleButton}
是绑定到“App”的第一个呈现的函数(当然它本身就是一个函数),并且{handleButton}
可以访问其父作用域中的const state
,这是“App”的第一个呈现周期中的空数组[]
因此,您在“<代码> {把手按钮} /代码>中,在<代码>状态< /代码>中,它仍然有一个值[](空数组),而<代码>使用效果< /代码>正在执行。
只要setState
在useffect
内部完成,就会执行App
的另一个渲染周期(由于本地状态更新,react会重新渲染组件)。将创建App
的新实例及其所有嵌套成员,由react
管理的state
和setState
除外
在第二次呈现App
之后,您的状态现在包含{handleButton}
的旧实例的引用(您将属性content
指定给App
之前呈现的按钮
),因此这些{handleButton}
仍然具有状态
值[](空数组闭包)
现在,当您单击任何按钮时,它的onClick
绑定到handleButton
的旧引用,该引用的state
值为空数组,因此当{handleButton}
完成执行时,它将调用setState
(由react框架提供的函数,不受重新渲染的影响)它再次将状态设置为一个空数组。最终,按钮消失了
现在要解决这个问题,您需要在每次对App
进行重新渲染时重新绑定{handleButton}
的新实例。您不能记忆{handleButton}
因为它取决于状态
。因此,您可以将按钮
组件的呈现委托给选项卡
,该选项卡在每次状态
更新后重新呈现自身
所以你可以这样做:
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>
);
}
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>
);
};
代码沙盒参考:
谢谢看到了,你明白了。但这里我们希望选项卡具有动态内容。这并不总是按钮,这就是我们使用内容道具的原因。看看这个例子,我的沙盒是它的简单版本。你可以看到选项卡窗格包含选项卡。内容:这迫使我在这里通过内容道具!!谢谢非常感谢您!!!!您能解释一下为什么它会起作用吗?谢谢您的回答。您的示例不起作用,但可能是我的函数中有错误。抱歉,但我已经更新了它。更改handleButton
函数中更新状态的方式所需的唯一一件事。尽管我仍在试图找出您的解决方案失败的原因oes不起作用…如果成功的话,我想分享一个解释…谢谢你很棒的解决方案。你明白了。但这里我们希望选项卡具有动态内容。它并不总是一个按钮,这就是我们使用内容道具的原因。看看这个例子,我的沙盒是它的简单版本。你可以看到选项卡窗格包含选项卡。内容:Check@Yousaf在上面的评论。我真的很感谢你的努力,能有一个像你一样热衷于分享和解决问题的开发人员真是太棒了。好吧,在这种情况下,我的另一个答案(我已经给出了两个答案)对这个问题没什么问题。
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>
);
};