[英]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.