Skip to content

How to implement shouldComponentUpdate with this.context? #2517

Closed
@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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions