[英]Unexpected Behavior After State Change in React Component
RenderImages = (): React.ReactElement => {
let selected = this.state.results.filter(x=>this.state.selectedGroups.includes(x.domain))
console.log(selected)
return(
<div className="results_wrapper">
{selected.map((r,i)=>{
let openState = (this.state.selectedImage==i)?true:false;
return(
<RenderPanel panelType={PanelType.large} openState={openState} title={r.domain+'.TheCommonVein.net'} preview={(openIt)=>(
<div className="result" onClick={openIt} style={{ boxShadow: theme.effects.elevation8}}>
<img src={r.url} />
</div>
)} content={(closeIt)=>(
<div className="panel_wrapper">
<div className="panel_content">{r.content}</div>
{this.RenderPostLink(r.domain,r.parent)}
<div onClick={()=>{
closeIt();
this.setState({selectedImage:2})
console.log('wtfff'+this.state.selectedImage)
}
}>Next</div>
<img src={r.url} />
</div>
)}/>
)
})}
</div>
)
}
当我更改“selectedImage”的状态时,我希望变量“openState”在我的 map() 函数中呈现不同的效果。 但它没有做任何事情。
Console.log 显示状态确实已成功更改。
更奇怪的是,如果我在 componentsDidMount() 中运行“this.setState({selectedImage:2})”,那么一切都完全按预期呈现。
为什么这不响应我的状态变化?
我尝试在组件状态变量中设置 openState ,但这也无济于事:
RenderImages = (): React.ReactElement => {
let selected = this.state.results.filter(x=>this.state.selectedGroups.includes(x.domain))
console.log(selected)
let html = selected.map((r,i)=>{
return(
<RenderPanel key={i} panelType={PanelType.large} openState={this.state.openState[i]} title={r.domain+'.TheCommonVein.net'} preview={(openIt)=>(
<div className="result" onClick={openIt} style={{ boxShadow: theme.effects.elevation8}}>
<img src={r.url} />
</div>
)} content={(closeIt)=>(
<div className="panel_wrapper">
<div className="panel_content">{r.content}</div>
{this.RenderPostLink(r.domain,r.parent)}
<div onClick={()=>{
closeIt();
let openState = this.state.openState.map(()=>false)
let index = i+1
openState[index] = true;
this.setState({openState:openState},()=>console.log(this.state.openState[i+1]))
}
}>Next</div>
<img src={r.url} />
</div>
)}/>
)
})
return(
<div className="results_wrapper">
{html}
</div>
)
}
https://codesandbox.io/s/ecstatic-bas-1v3p9?file=/src/Search.tsx
要进行测试,只需在搜索框中按 Enter。 然后单击 3 个结果中的 1 个。 当您单击“下一步”时,它应该关闭窗格,然后打开下一个窗格。 这就是我要在这里完成的任务。
我相信这是因为您在 map 函数中设置了 openState,它已经运行了。 我知道您认为该函数应该重新渲染,然后循环将再次运行,但我认为您需要在渲染之外的函数中设置 openState。
问题是,即使您可以从作为类组件的成员的组件访问this.state
,也没有什么可以使组件重新渲染。 在其他组件中制作组件是一种反模式,会产生意想不到的效果 - 正如您所见。
这里的解决方案是将 RenderImages 完全移动到一个单独的组件中并通过 props 或上下文传递所需的数据,或者将其转换为普通函数并在父组件的render()
中将其作为函数调用。
后者意味着代替<RenderImages/>
,你会做this.RenderImages()
。 而且由于它不再是一个组件而只是一个返回 JSX 的函数,我可能会将它重命名为renderImages
。
我厌倦了一遍又一遍地看它,但无法理解为什么它不能以任何干净的方法工作。
话虽如此,我能够通过“hack”使其工作,即在渲染完成后为selectedImage
显式调用openIt
方法。
RenderImages = (): React.ReactElement => {
let selected = this.state.results.filter((x) =>
this.state.selectedGroups.includes(x.domain)
);
return (
<div className="results_wrapper">
{selected.map((r, i) => {
let openState = this.state.selectedImage === i ? true : false;
return (
<RenderPanel
key={i}
panelType={PanelType.medium}
openState={openState}
title={r.domain + ".TheCommonVein.net"}
preview={(openIt) => {
/* This is where I am making explicit call */
if (openState) {
setTimeout(() => openIt());
}
/* changes end */
return (
<div
className="result"
onClick={openIt}
style={{ boxShadow: theme.effects.elevation8 }}
>
<img src={r.url} />
</div>
);
}}
content={(closeIt) => (
<div className="panel_wrapper">
<div className="panel_content">{r.content}</div>
{this.RenderPostLink(r.domain, r.parent)}
<div
onClick={() => {
closeIt();
this.setState({
selectedImage: i + 1
});
}}
>
[Next>>]
</div>
<img src={r.url} />
</div>
)}
/>
);
})}
</div>
);
};
看看这个 codeandbox 。
@Spitz 的答案是正确的,但没有遵循完整的解决方案。
您遇到的问题是面板的useBoolean
不会根据传递的openState
值更新其状态。
如果您将以下代码添加到 panel.tsx,那么一切都会如您所描述的那样工作:
React.useEffect(()=>{
if(openState){
openPanel()
}else{
dismissPanel();
}
},[openState, openPanel,dismissPanel])
这是什么东西做的是建立在同步的效果isOpen
状态在RenderPanel
与openState
是作为一个道具传递RenderPanel
。 这样,当面板在大部分情况下控制自己时,如果父级更改 openState,它将更新。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.