2

I want to dispatch an action to update my application's global state with the currently signed in user. When a user clicks a button, an event handler fires that is registered in mapDispatchToProps, and then I want to dispatch an action (article.updateAuthor(currentUser)). However, In mapDispatchToProps I have no access to the state to get the current user, and I do not want to arbitrarily pass the currently signed in user to a component prop -- only to be passed to the click event above, which will then pass the user data to a dispatched action.

I know I can dispatch an action with a redux thunk which gives me access to the state. However, this seems rather heavy handed seeing as there are no API calls being made.

Has anyone ran into this problem before?

GibboK
  • 71,848
  • 143
  • 435
  • 658
robskrob
  • 2,720
  • 4
  • 31
  • 58

4 Answers4

6

In the connect function provided by react-redux you have four optional arguments you can use:

  • mapStateToProps
  • mapDispatchToProps
  • mergeProps (this one will give you what you want)
  • options

From the documentation for mergeProps:

If specified, it is passed the result of mapStateToProps(), mapDispatchToProps(), and the parent props. The plain object you return from it will be passed as props to the wrapped component. You may specify this function to select a slice of the state based on props, or to bind action creators to a particular variable from props

So in your case you'll use mapStateToProps to access what ever portion of the global state you need. The result will be passed to mergeProps as the stateProps argument.

In mergeProps do what you need to do with this state, use the result of dispatchProps (or destructure dispatch from it) and return the object you need in the component you want to connect to.

mattr555
  • 27
  • 7
KA01
  • 4,191
  • 3
  • 19
  • 26
4

You could consider importing your store and use store.getState(), in this way you have access to your "global" store.

But please keep in mind that this approach is not reccomanded and you rather should use mergeProps as suggested by @markerikson answer if you are using connect.

getState()

Returns the current state tree of your application. It is equal to the last value returned by the store's reducer. Source.

Pseudo code:

import store from 'store'
store.getState().yourReducer.user.current
GibboK
  • 71,848
  • 143
  • 435
  • 658
2

You can't. mapDispatch is deliberately not given access to the state, because you generally use mapDispatch to create bound functions, and doing so based on state would continually cause those functions to get re-created when the state changes.

If you really want to do that, you can use the third argument to connect, known as mergeProps, but this should generally only be used as a last resort.

The recommended approaches are:

  • Extract relevant data in mapState, and have the component call this.props.someFunction(this.props.someRelatedValue)
  • Use a thunk and access the state in there

Overall, accessing the state in a thunk is a completely valid and supported capability. Some people have raised concerns about whether doing so is a good idea, and I addressed those concerns in my blog post Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability.

markerikson
  • 63,178
  • 10
  • 141
  • 157
  • Why should `mergeProps` be used as a last resort? Dan Abramov has a cautionary note on using a thunk to simply access the global state. Can you address this point? "Don’t abuse this pattern. It is good for bailing out of API calls when there is cached data available, but it is not a very good foundation to build your business logic upon. If you use getState() only to conditionally dispatch different actions, consider putting the business logic into the reducers instead." https://stackoverflow.com/questions/35411423/how-to-dispatch-a-redux-action-with-a-timeout/35415559 – robskrob Jun 07 '17 at 11:47
  • The most common use for `mergeProps` is to create functions that close over values from the Redux state. However, `mergeProps` will be re-run every time the state changes, thus creating new functions every time. That's a perf issue both for creating functions, and the fact that they're new references, since those will cause shallow equality checks to fail (including `PureComponent` and `shouldComponentUpdate`). As for Dan's comments, I addressed them in the blog post I linked. – markerikson Jun 07 '17 at 15:37
1

It is ridiculous that the state cannot be accessed in mapDispatchToProps.

There is an alternative to redux, the Dynadux, which is simple and solves Redux's cumbersome.

Also, ispatches can be fired from reducers without Thunk.

Dennis T
  • 109
  • 4