Nico Schneider
Nico Schneider

Reputation: 173

Re-rendering of connect()ed component based on mapStateToProps output

In a React + Redux based project, I have a connect()ed component which checks user permissions via an API fetch. Fetched permissions are stored in the Redux store.

The component basically looks like <Can check="...">...</Can>, which talks to our API (via Redux actions) to resolve the check. If the permission is granted, this.props.children is rendered, null otherwise.

For that, mapStateToProps() computes a passes prop from authorization data in the store, which is checked in <Can />s render() method. I utilize the ownProps parameter to mapStateToProps() to get the "stuff to check" and compute the passes flag.

There's a bit of caching going on so I don't re-fetch on every component mount, and it basically works. Sometimes, though, the component will not re-render when the passes prop updates to true (it will however render after navigating away - using react router - and back again, so basically if the component is re-mounted).

Do connect()ed components re-render if the output from mapStateToProps() changes? The docs for react-redux's connect() say this:

If ownProps is specified as a second argument, its value will be the props passed to your component, and mapStateToProps will be re-invoked whenever the component receives new props.

Does that mean that passing in ownProps changes the rendering to only re-render if props change, or in any other way? How can I understand the note regarding memoization/returning a function from mapStateToProps(), or is that not even related?

Thank you

Upvotes: 13

Views: 10034

Answers (2)

markerikson
markerikson

Reputation: 67459

Several things to know here:

  1. connect will shallow-compare the output of the last mapState call to the current mapState call. If nothing changed, it will not re-render the wrapped component.
  2. By default, connect will only run mapState when the store notifies subscribers. However, if your mapState function is declared as taking two parameters, connect will pass in the wrapped component's props as the second arg, allowing you to do things like state.somePerItemData[ownProps.itemId]. It also then calls mapState any time the incoming props differ, as that may affect the output of mapState.
  3. Reselect's default memoization only keeps a single cached value per selector function. If you have a component that is instantiated multiple times, and all instances are sharing the same selector function instance, then the selector's memoization probably won't work the way you want, because each component instance is probably calling it with different inputs (such as their own props). So, as a heavily advanced optimization, you can actually pass a factory function as the mapState argument, which could create a unique selector function instance for each component instance.

All that said, I'm afraid I don't have a specific answer for your actual question about the component not updated. I'd probably need to see the code in more detail.

Upvotes: 8

Dan Abramov
Dan Abramov

Reputation: 268225

Do connect()ed components re-render if the output from mapStateToProps() changes? The docs for react-redux's connect() say this:

Output from a function can’t change by itself. Something must trigger this function to be re-evaluated in the first place.

If Redux state changes, mapStateToProps is re-evaluated.

If props received from parent component are shallowly unequal (have changed) and you use ownProps argument, mapStateToProps is also re-evaluated.

If mapStateToProps returned shallowly equal values to its last call, then React Redux will skip rendering. If it returned shallowly unequal values, the wrapped component will be re-rendered. It is assumed that mapStateToProps itself is a pure function.

Sometimes, though, the component will not re-render when the passes prop updates to true

Please create a minimal project reproducing this and file an issue with the relevant code example.

How can I understand the note regarding memoization/returning a function from mapStateToProps(), or is that not even related?

Not related.

Upvotes: 14

Related Questions