Skip to content
Advertisement

React Setstate callback called but render delayed

I just started to learn to react 2 days ago and I’m having a hard time with react’s setstate method, all I know is use revstate parameter if want to change state based on previous state, and callback parameter to be executed right after the state change (please correct me if this wrong), so I just change the array content (which I render it using javascript’s array.map) and I wish it renders right after the state is changed, it is changing but delayed, it only render after I do another click but the render method is called for any senpai out there thanks for the help.

Handle click for changing to render content based on index passed on my button “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>

Advertisement

Answer

Refactor your code and Approach the simpler way

Actually, you shouldn’t use the second param callback.

Whenever the state is changed, the life cycle of React Js will re-render it properly (See the image below to clarify in detail ^^!)

There are some things to note:

  • Move the content of each item in torender accordingly –> This is clearer about the initial data as well as it should not be mutated.

  • Default clickeditem is one of the items in torender, for example, the first item.

  • After that, you just control the content to be rendered in this way

    ___________ 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>

enter image description here

Advertisement