簡體   English   中英

React 組件中狀態更改后的意外行為

[英]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狀態在RenderPanelopenState是作為一個道具傳遞RenderPanel 這樣,當面板在大部分情況下控制自己時,如果父級更改 openState,它將更新。

工作沙箱

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM