繁体   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