shouldComponentUpdate() is not being called

shouldcomponentupdate not being called
componentwillmount deprecated
react native lifecycle
react setstate callback
shouldcomponentupdate typescript
shouldcomponentupdate except
componentdidupdate setstate
shouldcomponentupdate implementation
Problem

I've parent class which contains list of items and renders component for each item of the list. When some item has been changed (even only one), all items in the list are being rerendered.

So I've tried to implement shouldComponentUpdate(). I am using console.log() to see if it is called but I can't see the log. I've found question shouldComponentUpdate is not never called and tried to return return (JSON.stringify(this.props) !=JSON.stringify(nextProps)); but component still renders itself again. So I've tried just to return false (like do not ever update) but it still does. As the last try I've used PureComponent but it is still being rerendered.


Question

How can I stop children re-rendering if the parent list changes and why is ShouldComponentUpdate never called?


Edit

I've noticed something what I didn't mention in question, I'm sorry for that. I am using context. If I don't use context -> it's ok. Is there any chance to stop re-render while using context? (I'm not using context on updated item - values of context didn't change).


Example

I've parent class which iterates list and renders TaskPreview component for each item of list:

class Dashboard extends React.Component
{
   constructor(props) {
      this.state = {
         tasks: {},
      };
   }

   onTaskUpdate=(task)=>
      this.setState(prevState => ({
         tasks: {...prevState.tasks, [task._id]: task}
      }));

   // ... some code

   render() {
      return (
         <div>
             {(!Object.entries(this.props.tasks).length)
                ? null
                : this.props.tasks.map((task,index) => 
                     <TaskPreview key={task._id} task={task} onChange={this.onTaskUpdate}/>
             })}
         </div>
      )
   }
}

and I've children TaskPreview class:

class TaskPreview extends React.Component
{
   shouldComponentUpdate(nextProps) {
        console.log('This log is never shown in console');
        return false; // just never!
   }

   render() {
        console.log('task rendered:',this.props.task._id); // indicates rerender
        return(<div>Something from props</div>);
   }
}

TaskPreview.contextType = TasksContext;
export default TaskPreview;

As @Nicolae Maties suggested I've tried to use Object.keys for iteration instead of direct map but it still doesn't call "shouldComponentUpdate" and still being re-rendered even if there is no changes.

Updated code:

render() {
      return (
         <div>
             {(!Object.entries(this.props.tasks).length)
                ? null
                : Object.keys(this.props.tasks).map((key,index) => {
                     let task = this.props.tasks[key];
                     <TaskPreview key={task._id} task={task}/>
                }
             })}
         </div>
      )
   }

Instead of return (JSON.stringify(this.props) != JSON.stringify(nextProps)); in your shouldComponentUpdate() life cycle, try specifying tasks object like this return (JSON.stringify(this.props.tasks) != JSON.stringify(nextProps.tasks));

shouldComponentUpdate isn't called when wrapping React , GitHub is home to over 50 million developers working together to host and shouldComponentUpdate() wasn't called, and render InputContainer was Not only is this really expensive as React throws away that part of the  After some investigation I discovered this.setState is called several times inside Mirror(Input), (with the expected values), however shouldComponentUpdate is not called as expected: To workaround this issue I checked for failures inside the setState callback, & re-called setState if required.

Maybe react is creating new instances of your component and replaces the old instances with them. That's why you're probably not getting your lifecycle method invoked. That can happen if the key property you're assigning in the map always changes.

shouldComponentUpdate / render not called after this.setState , GitHub is home to over 50 million developers working together to host and review code, shouldComponentUpdate / render not called after this. setState({state, context}, () => { if (this.state.state !== state || this.state.context  There is a bug where shouldComponentUpdate is not being called. In our case it caused bugs to be discovered only in production mode, since the app was rendered on every small change in development mode. This is also a performance hit (which is important is development too).

use from pureComponent and array as state:

class Dashboard extends React.PureComponent
{
   constructor(props) {
      this.state = {
         tasks: this.props.tasks
      }
   }

   onTaskUpdate=(task)=>
      this.setState(prevState => ({
         tasks: [...prevState.tasks, task] // render only new task
      }));

   render() {
       const {tasks} = this.state
      return (
         <div>
               {tasks.map(task => <TaskPreview key={task._id} task={task} />)}
         </div>
      )
   }
}

class TaskPreview extends React.PureComponent
{
   render() {
        console.log('task rendered:',this.props.task._id); // indicates rerender
        return(<div>Something from props</div>);
   }
}

React.Component – React, shouldComponentUpdate() is invoked before rendering when new props or state are being received. Defaults to true . This method is not called for the initial  The next method in the Update life cycle is shouldComponentUpdate (). This method allows your Component to exit the Update life cycle if there is no reason to apply a new render. Out of the box, the shouldComponentUpdate () is a no-op that returns true. This means every time we start an Update in a Component, we will re-render.

In the shouldComponentUpdate() method of your TaskPreview component, you should check if the next props have changes in comparison to the current props. Then if there are changes, return true to update the component, otherwise false.

The following example compares all the fields of props object with the new props object. But you can only check the props you are interested in.

shouldComponentUpdate(nextProps) {
    return  !!(Object.keys(nextProps).find(key => nextProps[key] !== this.props[key]));
}

React shouldComponentUpdate demystified, This process is called tree reconciliation. The root of the tree evaluated This is where shouldComponentUpdate() comes into play. You can tell React not to render rows that do not need to be using this method. class ListItem  The React team called shouldComponentUpdate a performance escape hatch for a reason — and you’d better bloody well hope that an escape hatch isn’t meant for regular use. Of course, “probably not” doesn’t mean “definitely not”.

I tried with below code snippet, shouldComponentUpdate worked as I expected. Could you share your Dashboard initial props ?

class Dashboard extends React.Component {
  constructor(props) {
    this.state = {
      tasks: {}
    };
  }

  onTaskUpdate = task =>
    this.setState(prevState => ({
      tasks: { ...prevState.tasks, [task._id]: task }
    }));

  // ... some code

  render() {
    return (
      <div>
        {!Object.entries(this.props.tasks).length
          ? null
          : Object.keys(this.props.tasks).map((key, index) => {
              let task = this.props.tasks[key];
              return (
                <TaskPreview
                  key={task._id}
                  task={task}
                  onChange={this.onTaskUpdate}
                />
              );
            })}
      </div>
    );
  }
}

class TaskPreview extends React.Component {
  shouldComponentUpdate(nextProps) {
    console.log("This log is never shown in console");
    return nextProps.task._id != this.props.task._id;
  }

  render() {
    console.log("task rendered:", this.props.task); // indicates rerender
    return (
      <button onClick={() => this.props.onChange(this.props.task)}>
        Something from props
      </button>
    );
  }
}

my initial props for Dashboard component is :

 <Dashboard  tasks={{test:{_id:'myId', description:'some description'}}}/>

Using shouldComponentUpdate() · react-indepth, Out of the box, the shouldComponentUpdate() is a no-op that returns true . When props or state is updated React assumes we need to re-render the content. This is why the React team calls the mixin pure, because it will not properly  shouldComponentUpdate() is invoked before rendering when new props or state are being received. Defaults to true. This method is not called for the initial render or when forceUpdate() is used. This method only exists as a performance optimization. Do not rely on it to “prevent” a rendering, as this can lead to bugs.

Should I use shouldComponentUpdate?, React provides a lifecycle method called shouldComponentUpdate, createElement , but the true form of render() 's output is just an object In fact, my experience has been that a 10 times speedup is not unusual at all. The first solution used to prevent a component from rendering in React is called shouldComponentUpdate. It is a lifecycle method which is available on React class components. Instead of having Square as a functional stateless component as before: const Square = ({ number }) => <Item>{number * number}</Item>;

I wish I knew these before diving into React, Each component has a method called shouldComponentUpdate and it is called In general, it is not reliable to use this.state inside the setState method. Updating: component is being re-rendered, can be caused by changes of props or state. ReactJS Events: “Pooling”, “Nullification”, & event.persist(). Additionally we have componentDidUpdate () which is invoked immediately after updating occurs and is not called for the initial render: componentDidUpdate (prevProps, prevState, snapshot) Practically we can manipulate the DOM in componentDidUpdate () method. In addition we can create side effects and do network requests.

React.Component, shouldComponentUpdate() is invoked before rendering when new props or state are being received. Defaults to true . This method is not called for the initial  If the shouldComponentUpdate function is not implemented, or it decided that the component should update in this render cycle, another life-cycle function will be called. This function is commonly

Comments
  • You are using .map() over an object, You should do Object.keys(this.props.tasks/this.props.state).map()..... Or use Object.entries as above
  • @NicolaeMaties I've tried your solution but nothing has changed. Provided an example in original post.
  • Thank you for response. I've tried this too but the problem is that shouldComponentUpdate is not being called at all.
  • Key is task._id which is not being changed.
  • Does array state really helps at all? I would like to use Object because I'm using Object keys in a lot of functions. Accessing value from object by a key (like tasks[index]) is constant time complexity O(1) but find in array (like tasks.find(...)) is linear time complexity O(n) which is much slower.
  • @JaxProx, find in array by index is constant time complexity O(1) like tasks[index].
  • @JaxProx, finding in object(dictionary) by key is equal to finding item in array by index.
  • Are you sure? As I know the find(()=> something === anything) tests every value by a function which have you specified (it means it will do it N times which is O(N)). Btw. at this moment I've tried to set let tasks = Object.values(this.props.tasks); and then loop it by map() as you wrote but It's still re-rendering.
  • @JaxProx, you dont need finding in array by "string compare". for example :{tasks.map(task => <TaskPreview onClick={() => handleClick(task.index)} />
  • Thank you for your time and response but it still doesn't help. Your idea is right but console.log in that function is not even being fired so I think the whole function is not called at all.