0

I am setting redux state in my componentDidMount function and then trying to access immediately and am not able to. I've stripped out some unnecessary complexity, but here's the basic setup:

// URL page?id=1

componentDidMount() {
  this.props.setFilter({ location: this.props.location.search.id) });
  console.log('in mount', this.props.filter);
}

// Action

export function setFilter(filterData) {
  return {
    type: SET_FILTERS,
    payload: filterData
  };
}

// Reducer 

export default function(state = INITIAL_STATE, action = {}) {
  switch(action.type) {
  case SET_FILTERS: {
    const newState = { ...state, filter: action.payload };
    console.log('reducer state', newState);
    return newState;
  }
  ...
}

This will output

reducer state { location: 1 }
in mount {}

but if I change the componentDidMount to

componentDidMount() {
  this.props.setFilter({ location: 1 });
  setTimeout(() => { console.log(this.props.filter), 0; });
}

it works as expected and outputs

reducer state { location: 1 }
in mount { location: 1 }

Why would this be?

Thanks!

Z2VvZ3Vp
  • 7,033
  • 6
  • 21
  • 35

1 Answers1

1

this.props is not updated directly by setFilter.

The action dispatched to the store triggers mapStateToProps to re-run, collect the new value, and merge it into the component props.

console.log('in mount', this.props.filter); runs before this cycle is complete.

setTimeout(() => { console.log(this.props.filter), 0; }); runs after this cycle is complete.

try this..

componentDidMount() {
  const propsCopy = this.props;
  this.props.setFilter({ location: 1 });
  console.log("before", this.props === propsCopy);
  setTimeout(() => { console.log("after", this.props === propsCopy) }, 0);
}

you'll get before true & after false.

so although the dispatch is synchronous, the props objects before and after the setTimout are different, and it's only the new props that have the filter set.

lecstor
  • 5,619
  • 21
  • 27
  • [I thought dispatch was synchronous?](https://stackoverflow.com/questions/43276291/is-store-dispatch-in-redux-synchronous-or-asynchronous) It seems to be synchronous everywhere else and I can rely on it. What's the difference here? – Z2VvZ3Vp Dec 05 '18 at 05:18
  • Can I reliably use the timeout hack to get the props change from the props change? – Z2VvZ3Vp Dec 05 '18 at 08:19
  • I wouldn't, it's certainly not best practice. Why is it needed? Can you not just default to `location: 1` wherever that state value is being used? – lecstor Dec 05 '18 at 10:28
  • Well, the default will be different based on user, the page this used on, and/or URL search params. – Z2VvZ3Vp Dec 05 '18 at 18:07
  • so you can calculate the default filter from user state or url props, not need to set filter state explicitly. – lecstor Dec 05 '18 at 22:27