Rob Wise
Rob Wise

Reputation: 5120

react-redux can update child smart components without updating parents?

Imagine the following React structure:

SmartComponentA -> DumbComponentB -> SmartComponentC

Also imagine that SmartComponentA and SmartComponentC each get connected to different slices of the state in their mapStateToProps functions.

Lastly, imagine that we put a console.log in the render method of each of these components.

When I actually try this, on the first render, I see that all components log as expected. But then if I change the data for SmartComponentC, I only see a single log message (C's log message), and I don't see SmartComponentA or DumbComponentB logging anything. How is that possible? How is react-redux getting React to update a child without updating its parents?

I would have assumed that the overriding of shouldComponentUpdate inside of the connect method would mean SmartComponentA would not get re-rendered (since its slice of the state didn't change), and therefore would cause a short-circuiting that would prevent SmartComponentC from getting re-rendered. While connect's implementation is not the same as the pure render mixin, both work by changing shouldComponentUpdate, but the pure render docs clearly state that React will "bail out" (as they put it) if a parent doesn't need to re-render:

for C2's subtree and C7, it didn't even have to compute the virtual DOM as we bailed out on shouldComponentUpdate.

source

If my question still isn't clear, here is sort of pseudo-code for the setup, and I'm asking why I can keep typing in C's input and it only log's C's messages to the console and not A's and B's (why is it not short-circuiting)?

//////////////////////////////////////////////

const SmartComponentA = (props) => {
  console.log('rendering SmartComponentA');

  return <DumbComponentB bData={props.bData} />;
};

const mapStateToProps = (state) => { bData: state.bData };

export default connect(mapStateToProps)(SmartComponentA);

//////////////////////////////////////////////

const DumbComponentB = (props) => {
  console.log('rendering DumbComponentB');

  return (
    <div>
      {props.bData}
      <SmartComponentC />
    </div>
  );
}

export default DumbComponentB;

//////////////////////////////////////////////

const SmartComponentC = (props) => {
  console.log('rendering SmartComponentC');

  return (
    <div>
      <input value={props.cValue} onChange={props.changeCValue} />
    </div>
  );
}

const mapStateToProps = (state) => { cValue: state.cValue };

export default connect(mapStateToProps, { changeCValue })(SmartComponentC);
//////////////////////////////////////////////

On the first render I see all log messages, then if I keep typing in the input, I only see C's log message each time I press a key.

Upvotes: 2

Views: 558

Answers (1)

Jack Noble
Jack Noble

Reputation: 2098

Prop changes trigger the React component lifecycle which will typically trigger the lifecycle of each child component unless -- as you observe, the process can be short-circuited by shouldComponentUpdate.

But prop changes aren't they only thing that triggers the component lifecycle -- state changes do too. And that's how connect function works. The Connect component subscribes to the store and on any store change checks to see if it will update the smart component's props (based on mapStateToProps). If so it will set it's own state triggering the lifecycle functions for the Connect component and it's child.

Upvotes: 3

Related Questions