1

I'm trying to implement shouldComponentUpdate on some of my components for sake of performance. We do it the usual way - have an immutable model and do reference comparison. It works for the most part, but I keep wondering if I'm handling functions in props correctly.

Essentially, my question is: in shouldComponentUpdate, what do you normally do with functions in props? Do you ignore them altoghether, or do you try to compare them somehow?

Here's an example:

var Inner = React.createClass({
    shouldComponentUpdate(newProps){
        return ???
    },

    render(){
        return <div onClick={this.props.onClick} />;
    }
});

var Outer = React.createClass({
    getInitialState(){ return {value: 0}; },

    render(){
        var val = this.state.value;
        return  <div>
                    <span>{val}</span>
                    <Inner onClick={() => { this.setState({value: val + 1}) }} />
                </div>
    }
});

Suppose I need the Inner component to have shouldComponentUpdate (suppose it is rendered many times and it is hard to render). How would you implement it? I tried the following:

1) this.props.onClick === newProps.onClick - works if you keep passing the same method, but it wouldn't work in this example, since the method is created inline.

2) this.props.onClick.toString() === newProps.onClick.toString() - works if the function doesn't have anything stale in closure - wouldn't work here, since val is in the function's closure.

3) As Michael pointed out, you can ignore functions in shouldComponentUpdate, with the same problem as 2) has.

All of these approaches make it possible to introduce subtle bugs, is there a more foolproof way of doing this? I know it's easy to rewrite this example to work, but ideally I'd like to be able to extract the shouldComponentUpdate behavior into a mixin that's as robust as possible, not lending itself to these problems.

2 Answers 2

2

I'm not convinced it is really ever desirable to skip over functions in your shouldComponentUpdate, but admit that it's sometimes unavoidable.

Firstly though in regards to your first point on inline methods. This will always give you a hard time when it comes to safely and efficiently re-rendering components, so I would advise against using inline methods wherever possible. Your example can achieve the same thing written like so:

class Inner extends React.PureComponent {
  // PureComponent performs the shallow equal check
  render(){
    return <div onClick={this.props.onClick} />;
  }
};

class Outer extends React.Component {

  constructor(props) {
    this.state = { value: 0 };
  }

  increment = () => {
    this.setState({ value: this.state.value + 1 });
  }

  render() {
    return (<div>
      <span>{val}</span>
      <Inner onClick={this.increment} />
    </div>);
  }
};

If you've exhausted this option and it just doesn't look like that will work for the use case you have then you can certainly write in a custom shouldComponentUpdate to skip over functions for you and I think that would be the best solution. Just make sure each time that there are no nasty surprises; if your function has dependencies that change in the component lifecycle then re-think your approach or pass those dependencies down as regular props (which will trigger an update) if they aren't already.

Here's a helpful package that you could use (although I haven't tried it out yet). Good luck!

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for this insight. I'll definitely stop using inlined functions for props.
0

Quick idea is to loop through all keys, and typeOf is a function then don't check, if not -> then check. I doubt there's a lot of props children on the first level, so shouldn't be slow.

Also maybe underscore compare object http://underscorejs.org/#isEqual will work, although, I haven't checked

1 Comment

So you're saying I should ignore functions in shouldComponentUpdate. That has the same drawback as the example in 2) - next time you render Outer, inner would not be re-rendered, therefore clicking on the div won't increment the counter anymore (val in the function's closure is still the same).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.