Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 880
Nico Schneider Avatar asked Jul 04 '16 17:07

Nico Schneider


2 Answers

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.

like image 134
Dan Abramov Avatar answered Nov 15 '22 02:11

Dan Abramov


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.

like image 30
markerikson Avatar answered Nov 15 '22 02:11

markerikson