React-redux: Expose context in mapStateToProps

Created on 8 Jan 2017  路  18Comments  路  Source: reduxjs/react-redux

I'm not satisfied with the answer to the issue here.
Also see this SO question for the use case.

I don't want the user/developer to have to pass Id to every single nested component just because context isn't exposed.

I can't get the current Object in a list based on Id without having to pass id from the parent to every component so I can access it in ownProps which is horrible. What I have to do at the moment is create a wrapping component that uses context and pass the prop down to each component . I need access to context in mapStateToProps.

Allowing me to pass context in my library so that the developer doesn't have to pass down id to every nested component would solve this.

Most helpful comment

You can solve this in userland by making your own wrapper on connect + getContext and use that in your app instead of vanilla connect. It'd look something like

export default function connectWithMyId(...args) {
  return compose(
    getContext({ myId: PropTypes.string }),
    connect(...args)
  )
}

And then myId would be available as a prop in your mapStateToProps.

All 18 comments

I don't see this happening in react-redux. The amount of complexity to support custom context props is pretty high. connect() would have to allow you to define what contextTypes you expect it to consume. Then the selector logic that looks at function argument count would have to get updated to handle 3-arg scenarios; this would have a trickle-down effect on reselect package which also deals with function arity. Then there's the inconsistencies in dealing with context values changing; there are no lifecycle hooks for context like componentWillReceiveProps. This is a significant increase in complexity.

My advice is to use recompose's getContext to expose your context value as a prop to any component that needs it.

You can solve this in userland by making your own wrapper on connect + getContext and use that in your app instead of vanilla connect. It'd look something like

export default function connectWithMyId(...args) {
  return compose(
    getContext({ myId: PropTypes.string }),
    connect(...args)
  )
}

And then myId would be available as a prop in your mapStateToProps.

@jimbolla I get the error getChildContext is not a function. Am I missing something?

Whoops. I put withContext in the example when it should be getContext. This is why I shouldn't chose before coffee.

@jimbolla That worked and is exactly what I needed. Thank you so much. If you want the bounty on SO I will award it to you if you copy that answer. If not, I shall post the answer and link here.

Sure, I love fake internet points. lol.

Thanks @jimbolla for this answer, it was exactly what I was looking for. Question: what kind of performance hit should we expect from always using props now in our mapStateToProps and mapDispatchToProps?

@neurosnap : erm... "performance hit", as compared to _what_? :)

Not using props in mapStateToProps and mapDispatchToProps travels a different code path for optimizations.

https://github.com/reactjs/react-redux/blob/master/src/connect/wrapMapToProps.js#L20

That's just whether your map* functions will be called _only_ when the store updates, or also any time props from a parent component change. You almost never want mapDispatch to depend on props. It's _fairly_ common to have mapState depend on props.

You may want to read through my post on performance-related concepts: http://blog.isquaredsoftware.com/2017/01/practical-redux-part-6-connected-lists-forms-and-performance/ .

Thanks for the info, I'll definitely give that blog post a read.

This might not be the best place to discuss this so I apologize in advance but I'm prompted to ask: what's the issue with mapDispatch depending on props?

You'd generally be re-creating functions each time. That can cause unnecessary re-rendering down the component tree, as well as the expense of actually creating those functions again and again.

It's one of several reasons why I prefer to use the object shorthand version of mapDispatch, so that I'm not even actually declaring a literal mapDispatch function: http://blog.isquaredsoftware.com/2016/10/idiomatic-redux-why-use-action-creators/ .

I've written a post about this approach, i find it pretty neat for some narrow cases in my app.
https://medium.com/@9276677/redux-composed-with-context-api-aec1c24f172e

@jimbolla in regards of your example:

export default function connectWithMyId(...args) {
  return compose(
    getContext({ myId: PropTypes.string }),
    connect(...args)
  )
}

React-redux does not seem to expose getContext. Is there any way to extract the custom React-redux context and pass the value as props?

Why does react-redux make it so hard?

@pronebird : several observations here:

  • Per Jim's comment, "You can solve this in userland by making your own wrapper on connect + getContext". In other words, getContext is a hand-waved component that he was suggesting _you_ write, not something that is built into React-Redux.
  • This issue was written several years ago, before the new React.createContext() API came out, and is actually discussing the old legacy this.context API
  • If you'd read the docs, you'd see that React-Redux v7 _does_ export both useStore() _and_ ReactReduxContext (per https://react-redux.js.org/using-react-redux/accessing-store ).
  • Complaining about a lack of capability in React-Redux, without actually explaining what your problem is and what you're trying to accomplish, is not helpful. If you can explain the actual issue you're facing, we might be able to offer some suggestions.

In addition, issues are intended to be meant for bug reports, not help desk discussions. But, if you can give some more details on what you're trying to do, I may be able to point you in the right direction.

@markerikson F man that's really a hostile reply with all these assumptions. Why'd you bother to answer at all if you tell me it's not a help desk or whatever?

I think it's a bug that there is no way to access the custom context outside of ReactReduxContext which I can't use within mapStateToProps.

In React, contextType is still available and this.context is not deprecated from what I know and Consumer/Provider model is not enforced. There are use cases for both.

I'd like to mix in some fields with the custom context and pass them along to all of the components within the tree and use that data in mapStateToProps.

@pronebird I don't interpret Mark's reply as hostile. It may not be the response you want, but that doesn't mean he has some ill will towards you.

Also, what you're trying to do is very much overloading the purpose of mSTP. Keep it simple and compose other functionality on top of it in other places. That's not a "bug" in React Redux, just a design decision.

@pronebird : I certainly wasn't deliberately being hostile. I generally go out of my way to be helpful and answer questions. However, I'll agree that I wasn't feeling terribly happy when I wrote that. Consider things from my point of view:

  • I woke up to see an email notification that there was a comment on a closed issue that is almost three years old. Necro-posting on threads is generally not a good sign.
  • While the thread does use the words "context" and "mapState", it has nothing to do with the current implementation of React-Redux, and is completely irrelevant.
  • Your comment shows no indication that you read through the thread and understood what was being said, or that you read the docs. (Looking at my initial response, this is the only "assumption" I see that I was making.)
  • You definitely didn't actually explain what your actual goal or problem was, which makes it impossible for me to actually provide a potential answer.
  • You immediately complained that "React-Redux makes it so hard", again without any prior conversation.

(And to top it off, I was answering first thing in the morning before I'd had my coffee.)

None of those are inclined to make me want to actually provide help.

But, having said all that, I'll actually go ahead and try to respond to your second comment, since you did _eventually_ provide a few details:

There's a lot of reasons why you can't access context values in mapState. For example:

  • The connect API was created years before the new React createContext API existed.
  • mapState runs _outside_ the React component tree, and even in a class component with contextType defined, the connect wrapper component has no way of accessing the context value inside _your_ wrapped component.

Also, I have no idea why you're talking about accessing ReactReduxContext at all. That only contains the Redux store instance, and nothing else. The only hypothetical reason for you to access that context instance is to grab the store instance, and we now have a useStore() hook for that.

If you need to pass custom values down to parts of your app, either put them directly in the Redux store state, or create your own custom context instance and provide that at the top of the component tree.

If you truly need to use some values from context in the process of extracting data from the Redux store, then I suggest you write a function component that reads from your own context via useContext(), and make use of those values to extract data via our useSelector() hook.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fuchsberger picture fuchsberger  路  3Comments

teosz picture teosz  路  4Comments

IbraheemAlSaady picture IbraheemAlSaady  路  3Comments

iammerrick picture iammerrick  路  3Comments

nainardev picture nainardev  路  3Comments