Edit: I was able to have the connected Child update on context change with the suggestion in the Redux troubleshooting of setting { pure: false }
however I needed to add this to all of the connected Components that are parents of the Child I want to be update. Seems odd and inefficient to have to do this to a lot of components.
First of all, I will describe what my desired end result is before diving into what I think the problem is.
I am wanting to access the params of react router from within a grandchild component of my Route component. There are several references that say it is not necessary to sync the current route with the store if you are not using "recording, persisting, and replaying user actions, using time travel" so I have avoided it so far. Dan Abramov states here that it is okay to keep them un-synced and to just use the route as the source of truth.
Similar questions that didn't have answers to my problems:
https://stackoverflow.com/a/36633892/2300773
Passing store through context with <Provider> not working in connect() based scenario
Since react-router does not provide the current params as context, I have opted to add the current param as a context from the Route component. I can access the context all the way down the component tree until I reach a Child that has been setup using connect(). The initial rendering of the page gives the Child the correct context. However, when the context changes, the Child does not get updated. connect() appears to be stripping away the context link.
Is this the desired behaviour of connect()? If so, how is a component supposed to access the route params without them being synced to the store?
Here is a JSFiddle showing a very minimal scenario where context isn't updated past the "connected" component.
Here is a bit of my actual code where I am sending the current route param as context from the Router component (/view/:viewSlug
). I have tried setting the component to { pure: false }
in connect as described in the redux troubleshooting but had no luck.
class App extends Component {
getChildContext() {
const { viewSlug } = this.props
return { viewSlug }
}
...
}
App.PropTypes = {
...
}
App.childContextTypes = {
viewSlug: PropTypes.string
}
...
export default connect(mapStateToProps)(App)
import Day from '../components/Day'
const mapStateToProps = (state, ownProps) => {
...
}
export default connect(mapStateToProps)(Day)
class Day extends Component {
...
}
Day.PropTypes = {
...
}
Day.contextTypes = {
viewSlug: PropTypes.string.isRequired
}
export default Day
Overview The connect() function connects a React component to a Redux store. It provides its connected component with the pieces of the data it needs from the store, and the functions it can use to dispatch actions to the store.
Yes but as I said, I don't need to deal with the mapping for each of my components if I do it once in the Context and use the hook of context instead. But every component (using that context) will has access to all the state in your redux store.
Connecting the Components React Redux provides a connect function for you to read values from the Redux store (and re-read the values when the store updates). The connect function takes two arguments, both optional: mapStateToProps : called every time the store state changes.
Could not find "store" in the context of "Connect(Home)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(Home) in connect options.
After a quick discussion with Dan Abramov, there doesn't appear to be a good way to pass params down to various deep components. (Discussion here). Once React Router 3.0 is released, this will be very trivial to implement correctly.
Currently the only workaround I found was to set the Child Component, as well as any Ancestor Components between the Child Component and the Route Component, as an "un-pure" component.
This can be done with the options parameter when using the connect() function.
export default connect(mapStateToProps, null, null, { pure: false })(MyComponent)
This information was found on the Redux Troubleshooting page
I have the same problem: I have a language param in a route path which I store as context and use in all components of my app. And when I connect redux store, context properties are not updating anymore.
As you mentioned, there is a { pure: false }
argument to force rerender the component more times than needed.
Another solution to not use context. You can use react-router-redux to save and update changed URL in Redux store. It has its own action LOCATION_CHANGE which has a payload object:
routing: {
locationBeforeTransitions: {
pathname: 'view/someView',
search: '',
…
}
}
For now it does not have params, but you can parse pathname
and get them in old school regexp way. :)
And please read this issue, there are some other workarounds: https://github.com/facebook/react/issues/2517
UPDATE:
There is another way — save your app state in redux store:
…
import { setCurLang } from '../actions/langActions';
const App = React.createClass({
componentWillMount(){
this.props.actions.setCurLang(this.props.params.curLang);
},
shouldComponentUpdate: function(nextProps) {
return nextProps.params.curLang !== this.props.params.curLang;
},
componentWillUpdate(nextProps) {
this.props.actions.setCurLang(nextProps.params.curLang);
},
render() {
return (
<div>
{/* here you can use your saved URL-param as prop,
and it will be updated each time react router changes */}
{this.props.curLang}
</div>
)
}
});
function mapStateToProps(state) {
return {
curLang: state.curLang
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators( { setCurLang }, dispatch)
}
}
export default connect(null, mapDispatchToProps)(App);
my reducer (this is simplified, of course you can use combineReducers
here):
const curLang = 'de';
export default function reducer(state = curLang, action) {
switch (action.type){
case 'SET_CUR_LANG':
return { ...state, cur: action.curLang }
default:
return state;
}
}
and action:
export function setCurLang(curLang) {
return {
type: 'SET_CUR_LANG',
curLang: curLang
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With