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.

Should mapStateToProps be called every time an action is dispatched?

See original GitHub issue

I was under the impression that mapStateToProps is called every time an action is dispatched which causes the store to change.

In my simplified example below, this doesn’t seem to be the case.

reducers.js

const foo = (state = true, action)  =>{
  switch (action.type) {
  case "foo" :
    return false;
  default :
    return state;
  }
}

const bar = (state = 0, action) => {
  switch (action.type) {
  case "bar" :
    return state + 1;
  default :
    return state;
  }
}


const rootReducer = combineReducers({
  foo,
  bar 
});

Container

import React, { Component } from 'react';
import { connect } from 'react-redux';

class Container extends Component {
  constructor(props) {
    super(props);
    this.onBarClick = this.onBarClick.bind(this);
  }

  onBarClick () {
    this.props.dispatch({
      type : "bar"
    }); 

    // I expect mapStateToProps to be called here 
    // which would allow me to check the value
    // of the updated props, and dispatch foo if the
    // condition is met

    if (this.props.isBarGreaterThanEqual3) {
      this.props.dispatch({
        type : "foo"
      }); 
    }   
  }

  render () {
    return (
      <div>
        <button onClick={this.onBarClick}>Click bar</button>
      </div>
    )   
  }
}

const mapStateToProps = (state) => {
  console.log("map state to props");
  return Object.assign({}, state, {
    isBarGreaterThanEqual3 : state.bar >= 3
  });
};

export default connect(mapStateToProps)(Container);

If it is wrong of me to expect mapStateToProps to be called after every dispatch, what is the recommended way to conditionally dispatch an action when the condition depends on the state of the store that has just been updated by another action?

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Reactions:2
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

93reactions
gaearoncommented, Feb 18, 2016

I think I (probably) see your point now. I haven’t run the app yet but from the source it looks like you expect this.props.isBarGreaterThanEqual3 to update instantaneously after you dispatch in the same event handler.

This is not how React works. In React, state changes (and Reacf Redux uses setState internally) are potentially asynchronous. This is because React batches update that happen during the same event handler. So calling dispatch will update the store state immediately but your components will be updated a bit later during the same tick, together.

Instead of assuming dispatch updates props synchronously, you can use componentWillReceiveProps to react to changes in the props when they happen.

  onBarClick() {
    this.props.dispatch({
      type : "bar"
    });
  }

  componentWillReceiveProps(nextProps) {
    if (
      !this.props.isBarGreaterThanEqual3 &&
      nextProps.isBarGreaterThanEqual3
    ) {
      this.props.dispatch({
        type : "foo"
      });
    }
  }

Note that also, in general, dispatching actions in response to store-dependant prop changes is an anti-pattern. If you want to react to an action, it is best to do so in reducer. If you want to calculate some state that depends on the store state, it is best to do this in a selector.

15reactions
gaearoncommented, Feb 18, 2016

So is it fair to say that mapStateToProps isn’t necessarily called after every dispatch, but it is only called once per batch of dispatched actions?

It is fair to say mapStateToProps is called after but not necessarily immediately after the dispatch. It is called when the component is about to re-render, which depends on whether React batches the updates or not. By default, React batches updates from event handlers.

Having said that, it’s not clear to me how I would update foo via a reducer when bar reaches 3?

Since one depends on the state of the other, maybe they should just be a single reducer.

const reducer = (state = { counter: 0, foo: false }, action) {
  switch (action.type) {
  case "bar":
    return Object.assign({}, state, {
      counter: state.counter + 1,
      foo: state.counter === 2
       // depending on your use case, it could also be:
       // foo: !state.foo && state.counter === 2 
       // foo: !state.hasEverBeenFooBefore && state.counter === 2 
       // etc
    });
  case "foo" :
    return Object.assign({}, state, {
      foo: true
    });
  default:
    return state
  }
}

Another, simpler, option is to keep them separate, and compute the result as derived data:

const bar = (state = 0, action) => {
  switch (action.type) {
  case "bar" :
    return state + 1;
  default :
    return state;
  }
};

const foo = (state = false, action)  => { 
  switch (action.type) {
  case "foo" :
    return true;
  default :
    return state;
  }
};

const reducer = combineReducers({
  foo,
  bar
});

const isFooishEnough = (state) => {
  return state.foo || state.bar >= 3;
}

In this case, we defined a selector called isFooishEnough. You can call it from your mapStateToProps and use that value instead. In general, this is the preferred pattern: whatever can be computed from Redux store state, should not be there.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Connect: Extracting Data with mapStateToProps - React Redux
It is called every time the store state changes. It receives the entire store state, and should return an object of data this...
Read more >
redux connect mapStateToProps not called when action is ...
When I put the store on window ( window.store=store ), add a console.log in the mapStateToProps, then in the console I dispatch an...
Read more >
React Redux
React Redux #291: Should mapStateToProps be called every time an action is dispatched? Stack Overflow: Cleaner/shorter way to update nested state in Redux?...
Read more >
MapStateToProps and MapDispatchToProps in React Redux
It also implements shouldComponentUpdate comparisons inside. Which means yes it fires every time when state changes but our component props will be changed...
Read more >
Connect | React Redux
If your mapStateToProps function is declared as taking one parameter, it will be called whenever the store state changes, and given the store...
Read more >

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