Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redux mapStateToProps called multiple times

I have this very simple Component, which is connected to redux state and returns {fruit, vegetables}. Everything works fine, but let's say I have a graph inside the Component and I if receive only updated vegetable from API the graph is being recreated each time.

Here's my component:

const Products = ({ fruit, vegetable }) =>
  <div className='Products'>
      <div>{vegetable.map(....logic here)}</div>
      <div>{Math.random()}</div> // this is to illustrate the component is rendering every time
      <Graph>Here will be a chart or a graph with fruit</Graph> //but it gets re-rendered even though there is no new fruit
  </div>

const mapStateToProps = (state) => {
  return {
    fruit: state.data.fruit,
    vegetable:  state.data.vegetable,
  }
}

export default connect(mapStateToProps)(Products)

It seems to me that every-time, no matter which states is updated it re-renders the whole components.

Is there a way to prevent that?

like image 647
angular_learner Avatar asked Apr 04 '17 09:04

angular_learner


People also ask

How often is mapStateToProps called?

As the first argument passed in to connect , mapStateToProps is used for selecting the part of the data from the store that the connected component needs. It's frequently referred to as just mapState for short. It is called every time the store state changes.

What is the difference between mapStateToProps () and mapDispatchToProps ()?

mapStateToProps() is a function used to provide the store data to your component. On the other hand, mapDispatchToProps() is used to provide the action creators as props to your component.

Is useSelector the same as mapStateToProps?

The significant difference between them is that mapStateToProps passes down multiple values as props, while useSelector takes the current state as an argument, returns the required data, and stores the returned value as a single variable instead of a prop.

When mapDispatchToProps is called?

If your mapDispatchToProps function is declared as taking two parameters, it will be called with dispatch as the first parameter and the props passed to the connected component as the second parameter, and will be re-invoked whenever the connected component receives new props.


1 Answers

When a React component gets rendered, the whole tree of components below it also gets rendered - at the exception of the components which shouldComponentUpdate hook returns false. So in your case, if the Products component gets rendered, it is normal that the Graph component also does.

You have two options here:

  • if your Products component does not use the fruit prop outside of the Graph component, you can connect directly your Graph component to the fruitstate, and use the pure option of the connect function to avoid re-renders when fruit does not change

  • you can define the shouldComponentUpdate hook in your Graph component to manually skip unnecessary renders, or use a helper library to do it for you, for example the pure helper of the recompose library

The first option is where optimizing react/redux apps / avoiding unnecessary renders generally starts: connect your components to the store at the lowest level where it makes sense. The second option is more of an escape hatch - but still often useful.

As you mention you use stateless components, you can use a higher-order component to benefit from the shouldComponentUpdate hook. To understand how this works, here's how a simple implementation of it could look like this:

function pure(BaseComponent, shouldUpdateFn) {
    return class extends Component {
        shouldComponentUpdate(nextProps) {
            return shouldUpdateFn(this.props, nextProps);
        }
        render() {
            return <BaseComponent { ...this.props } />;
        }
    }
}

This would give you a pure HOC that you could reuse over your app to avoid unnecessary renders: it works by wrapping your stateless component into a new component with the desired hook. You'd use it like so, for example:

export default pure(Graph, (props, nextProps) => props.fruit !== nextProps.fruit)

Still, i highly encourage you in having a look at recompose, which has more fine-grained implementations of this, and would avoid you to reinvent the wheel.

like image 115
VonD Avatar answered Sep 28 '22 12:09

VonD