question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Avoid unnecessary setState call when Route receives same props.

See original GitHub issue

I just encounter a situation. I have the below App component connected to Redux. The requestQuantity changes according to current processing XHRs , which causes the App re-render. Then the TopicList will re-render which shouldn’t happen. The TopicList is also connected to Redux and doesn’t depend on requestQuantity, based on the shallow comparison of Redux’s connect, changing of requestQuantity has nothing to do with the TopicList’s render.

class App extends Component {
  render() {
    const { requestQuantity } = this.props;
    return (
      <div>
        <Router>
          <Switch>
            <Route exact path="/" component={TopicList} />
            <Route path="/login" component={Login} />
            <Route path="/topics" component={TopicList} />
          </Switch>
        </Router>
        {requestQuantity > 0 && <Loading />}
      </div>
    );
  }
}

const mapStateToProps = (state, props) => {
  return {
    requestQuantity: getRequestQuantity(state)
  };
};

export default connect(mapStateToProps)(App);

I check the source code and find the Route’s componentWillReceiveProps will always call the setState, which set a new match object. It is the new match prop passed to the TopicList causing the Redux’s shallow comparison failed.

I think we can make some comparison before calling the setState. If the location, strict, exact and sensitive don’t change, we can skip the setState and just passing the old state to the components in Route. In this way , the components can render more efficiently especially when using with Redux and Mobx.

  componentWillReceiveProps(nextProps, nextContext) {
    warning(
      !(nextProps.location && !this.props.location),
      '<Route> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.'
    )

    warning(
      !(!nextProps.location && this.props.location),
      '<Route> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.'
    )

    // do some shouldComputeMatch logic before setState
    this.setState({
      match: this.computeMatch(nextProps, nextContext.router)
    })
  }

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
xuchaobeicommented, Mar 6, 2018

@johnnyreilly aha, you’re right, the workaround is by me.

1reaction
johnnyreillycommented, Mar 6, 2018

There’s a workaround here: https://stackoverflow.com/questions/47375973/react-router-causes-redux-container-components-re-render-unneccessary (perhaps by original poster?)

It would be great to know if this is considered “safe”:

Now my solution is creating a HOC :

// connectRoute.js
export default function connectRoute(WrappedComponent) {
  return class extends React.Component {
    shouldComponentUpdate(nextProps) {
      return nextProps.location !== this.props.location;
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

Then use connectRoute to wrap the containers used in Route:

const PostListWrapper = connectRoute(PostList);
const LoginWrapper = connectRoute(Login);

class App extends Component {
  render() {
    const { requestQuantity } = this.props;
    return (
      <div>
        <Router>
          <Switch>
            <Route exact path="/" component={PostListWrapper} />
            <Route path="/login" component={LoginWrapper} />
            <Route path="/topics" component={PostListWrapper} />
          </Switch>
        </Router>
        {requestQuantity > 0 && <Loading />}
      </div>
    );
  }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Avoid unnecessary setState call when Route receives same ...
I check the source code and find the Route's componentWillReceiveProps will always call the setState , which set a new match object. It...
Read more >
Prevent all children re-rendering every call of setState
To solve this issue you'll want to create an additional component for the row item. This way it will have a separate render...
Read more >
5 Ways to Avoid React Component Re-Renderings
1. Memoization using useMemo() and UseCallback() Hooks ... Memoization enables your code to re-render components only if there's a change in the props....
Read more >
React setState usage and gotchas - ITNEXT
To avoid unnecessary renders, calling setState() only when the new state differs from the previous state makes sense and can avoid calling setState()...
Read more >
How to work with React the right way to avoid some common ...
Specifically, calling setState() in an unmounted component means that your app is still holding a reference to the component after the component ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found