Skip to content

How to implement shouldComponentUpdate with this.context? #2517

@gaearon

Description

@gaearon

I know this.context is not officially there but quite a few libraries rely on it, and it seems like it's getting into shape with #2509.

I'm trying to understand how exactly shouldComponentUpdate is supposed to be implemented with context in mind. I noticed it accepts a third argument (nextContext) and I can extend PureRenderMixin to also check it:

  shouldComponentUpdate: function(nextProps, nextState, nextContext) {
    return !shallowEqual(this.props, nextProps) ||
           !shallowEqual(this.state, nextState) ||
           !shallowEqual(this.context, nextContext); // this will throw without context, read on
  }

Components that don't opt into this.context by not omitting contextTypes will not get this third argument, which is understandable.

However this presents a problem when we have a <Middle /> component in between between <Top /> context owner and <Bottom /> context consumer. If <Middle /> implements a restrictive shouldComponentUpdate, there is no way for <Bottom /> to react to <Top />'s context updates at all:

(fiddle)

var Bottom = React.createClass({
  contextTypes: {
    number: React.PropTypes.number.isRequired
  },

  render: function () {
    return <h1>{this.context.number}</h1>
  }
});

var Middle = React.createClass({
  shouldComponentUpdate: function (nextProps, nextState, nextContext) {
    return false;
  },

  render: function () {
    return <Bottom />;
  }
});

var Top = React.createClass({
  childContextTypes: {
    number: React.PropTypes.number.isRequired
  },

  getInitialState: function () {
    return { number: 0 };
  },

  getChildContext: function () {
    return { number: this.state.number };
  },

  componentDidMount: function () {
    setInterval(function () {
      this.setState({
        number: this.state.number + 1
      });
    }.bind(this), 1000);
  },

  render: function() {
    return <Middle />;    
  }
});

React.render(<Top />, document.body);

The same problem would occur if I tried to give Middle a generic context-aware shouldComponentUpdate as I wrote above, because Middle has no this.context unless it opts in.

This is possible to work around by adding contextTypes to Middle, but it doesn't look like a good solution. You'd need to explicitly add necessary contextTypes on every level with smart shouldComponentUpdate so it's too easy to slip up.

Will this be solved by #2112? Is there another solution in the meantime? What is the recommended way?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions