调用 React Setstate 回调但延迟渲染

Posted

技术标签:

【中文标题】调用 React Setstate 回调但延迟渲染【英文标题】:React Setstate callback called but render delayed 【发布时间】:2021-04-21 18:19:43 【问题描述】:

我 2 天前刚开始学习反应,我很难使用反应的 setstate 方法,我只知道如果想根据以前的状态更改状态,请使用 revstate 参数和回调参数在状态更改后立即执行(如果错误请纠正我),所以我只是更改数组内容(我使用 javascriptarray.map 渲染它)并且我希望它在状态更改后立即渲染,它正在改变但是延迟了,它仅在我再次单击后才渲染,但调用了渲染方法 感谢任何前辈的帮助。

处理单击以根据我的按钮“onClick”上传递的索引更改以呈现内容

class App extends React.Component 
     constructor(props)
        super(props)
        this.state = 
          clickeditem : -1
        
    this.torender = [
      
        display : "first",
        content : []
      ,
      
        display : "second",
        content : []
      
    ]


  handleclick = (i) =>
    this.setState(prevstate=>
      if (prevstate.clickeditem === -1) 
        return clickeditem : i
       else 
        return prevstate.clickeditem === i ? clickeditem : -1 : clickeditem : i
      
    ,() => 
      return this.state.clickeditem === -1 ? (this.torender[0].content = [], this.torender[1].content = [])
        : (this.state.clickeditem === 0) ? (this.torender[0].content = ["torender-0 content","torender-0 content"],this.torender[1].content = [])
          : (this.state.clickeditem === 1) ? (this.torender[1].content = ["torender-1 content","torender-1 content"],this.torender[0].content = [])
            : null
    )
   

  render()
    return(
      <div>
        <ul>
        
        this.torender.map((item,index) => 
          return(
            <li key = index>
              item.display
                <ul>
                  item.content.map((content,contentindex) => 
                    return(<li key = contentindex>content</li>)
                  )
                </ul>  
            </li>
          )
        )
        
        </ul>
        <button onClick=()=>this.handleclick(0)>first-button</button>
        <button onClick=()=>this.handleclick(1)>second-button</button>
      </div>
    )
  

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

【问题讨论】:

this.torender 不是反应状态的一部分,组件需要再次重新渲染才能看到您在上一个渲染周期中所做的突变。如果您将其作为状态的一部分,或者更好,只需在渲染 UI 时计算它的值,那么它应该可以工作。 【参考方案1】:

重构您的代码并使用更简单的方法

其实你不应该使用第二个参数callback

每当状态发生变化时,React Js 的生命周期都会重新正确渲染(详细看下图说明^^!)

有几点需要注意:

相应地移动torender中每个项目的内容-->这对初始数据更清楚,并且不应该被改变。

默认clickeditem torender中的一项,例如第一项。

之后,你只需控制要以这种方式呈现的内容

___________ The condition to call renderContent() method ______________
index === this.state.clickeditem && this.renderContent(item)

_____________renderContent() looks like below_____________
renderContent = (item) => 
return (
   <ul>
     item.content.map((content, contentindex) => 
       return <li key=contentindex>content</li>;
     )
   </ul>
 );
;

class App extends React.Component 
  constructor(props) 
    super(props);
    this.state = 
      clickeditem: 0
    ;

    this.torender = [
      
        display: "first",
        content: ["torender-0 content", "torender-0 content"]
      ,
      
        display: "second",
        content: ["torender-1 content", "torender-1 content"]
      
    ];
  

  handleclick = (index) => 
    this.setState(clickeditem: index);
  ;

   renderContent = (item) => 
    return (
      <ul>
        item.content.map((content, contentindex) => 
          return <li key=contentindex>content</li>;
        )
      </ul>
    );
  ;

  render() 
    return (
      <div>
        <ul>
          this.torender.map((item, index) => 
            return (
              <li key=index>
                item.display
                index === this.state.clickeditem && this.renderContent(item)
              </li>
            );
          )
        </ul>
        <button onClick=() => this.handleclick(0)>first-button</button>
        <button onClick=() => this.handleclick(1)>second-button</button>
      </div>
    );
  


ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"> </div>

【讨论】:

比我的解决方案更清洁,我认为如果您添加了关于 torender 突变以及为什么需要额外的渲染周期的类似解释,那么您将获得适当完整的答案。 @Phong 我发现这个组件生命周期非常有用,非常感谢 gbu!【参考方案2】:

问题

this.torender 不是反应状态的一部分,组件需要再次重新渲染才能看到您在上一个渲染周期中所做的突变。

解决方案

最好在渲染 UI 时计算它的值,然后它应该可以按照我怀疑的那样工作。

  handleclick = (i) =>
    this.setState(prevstate=>
      if (prevstate.clickeditem === -1) 
        return  clickeditem: i 
       else 
        return prevstate.clickeditem === i ?  clickeditem: -1  :  clickeditem: i 
      
    )
   

  render()
    const  clickeditem  = this.state;

    let torender = [
      
        display : "first",
        content : []
      ,
      
        display : "second",
        content : []
      
    ];

    if (clickeditem === -1) 
      torender[0].content = [];
      torender[1].content = [];
     else if (clickeditem === 0) 
      torender[0].content = ["torender-0 content","torender-0 content"];
      torender[1].content = [];
     else if (clickeditem === 1) 
      torender[1].content = ["torender-1 content","torender-1 content"];
      torender[0].content = [];
     else 
      torender = []; // <-- render nothing
    

    return(
      <div>
        <ul>
          torender.map((item,index) => 
            return(
              <li key = index>
                item.display
                  <ul>
                    item.content.map((content, contentindex) => (
                      <li key=contentindex>content</li>;
                    ))
                  </ul>  
              </li>
            )
          )
        
        </ul>
        <button onClick=()=>this.handleclick(0)>first-button</button>
        <button onClick=()=>this.handleclick(1)>second-button</button>
      </div>
    )
  

【讨论】:

以上是关于调用 React Setstate 回调但延迟渲染的主要内容,如果未能解决你的问题,请参考以下文章

JS每日一题: 如何理解react中setState?

[React] 子组件向父组件通信:回调函数

在回调函数中使用 setState 挂钩时反应过多的重新渲染

无法在 socket.io 回调 React.js 中调用 setState

在 React JS 的 this.setState 的回调中使用 this.setState?

即使在调用 setState 之后,React.js 子状态也不会重新渲染?