With react-router-redux, it appears as though the only way to get routing information is through props only. Is this right?
Here's roughly what I am doing in my app right now:
<Provider store={Store}>
<Router history={history}>
<Route path="/" component={App}>
<Route path="child/:id" />
</Route>
</Router>
</Provider>
App
const App = (props) =>
<div className="app">
<Header />
<Main {...props}/>
<Footer />
</div>
Main
const Main = (props) =>
<div>
<MessageList {...props}/>
</div>
MessageList
let MessageList = (props) => {
const {id} = props;
// now I can use the id from the route
}
const mapStateToProps = (state, props) => {
return {
id: props.params.id
};
};
MessageList = connect(mapStateToProps)(MessageList)
What I would like to do, is remove {...props} from all of my components, and turn MessageList into this:
let MessageList = (props) => {
const {id} = props;
// now I can use the id from the route
}
const mapStateToProps = (state) => {
return {
id: state.router.params.id
};
};
MessageList = connect(mapStateToProps)(MessageList)
Having to pass down props in everything feels like a big step back for how clean Redux made my application. So if passing params is correct, I'm wondering why thats preferable?
My specific case that brought this up:
I have an UserInput component that sends a message (dispatches a SEND_MESSAGE action). Depending on the current page (chat room, message feed, single message, etc) the reducer should put it in the correct spot. But, with react-redux-router, the reducer doesn't know about the route, so it can't know where to send the message.
In order to fix this I need to pass the props down, attach the id to my SEND_MESSAGE action, and now the otherwise simple UserInput is handling business logic for my application.
You can use the location prop to access the state you have passed. Visit React-Router for reference. When you want to access that state, you can do it by this. props.
STEP-1 import useStore first from react-redux and then getState() function is used to access store state.
Use the useLocation() hook to get the current route with React Router, e.g. const location = useLocation() . The hook returns the current location object. For example, you can access the pathname as location. pathname .
Rather than address your question (how to read the state), I will address your problem itself (how to dispatch different actions depending on the current route).
Make your UserInput
a presentational component. Instead of dispatching inside it, let is accept onSend
prop that is a callback provided by the owner component. The input would call this.props.onSend(text)
without knowing anything about Redux or routes.
Then, make MessageList
also a presentational component that accepts onSendMessage
as a prop, and forwards it to UserInput
. Again, MessageList
would be unaware of routes, and would just pass it down to <UserInput onSend={this.props.onSendMessage} />
.
Finally, create a couple of container components that wrap MessageList
for different use cases:
const mapDispatchToProps = (dispatch) => ({
onSendMessage(text) {
dispatch({ type: 'SEND_MESSAGE', where: 'CHAT_ROOM', text })
}
})
const ChatRoomMessageList = connect(
mapStateToProps,
mapDispatchToProps
)(MessageList)
const mapDispatchToProps = (dispatch) => ({
onSendMessage(text) {
dispatch({ type: 'SEND_MESSAGE', where: 'FEED', text })
}
})
const FeedMessageList = connect(
mapStateToProps,
mapDispatchToProps
)(MessageList)
Now you can use these container components in your route handlers directly. They will specify which action is being dispatched without leaking those details to the presentational components below. Let your route handlers take care of reading IDs and other route data, but try to avoid leaking those implementation details to the components below. It’s easier in most cases when they are driven by props.
Addressing the original question, no, you should not attempt to read the router parameters from Redux state if you use react-router-redux
. From the README:
You should not read the location state directly from the Redux store. This is because React Router operates asynchronously (to handle things such as dynamically-loaded components) and your component tree may not yet be updated in sync with your Redux state. You should rely on the props passed by React Router, as they are only updated after it has processed all asynchronous code.
There are some experimental projects that do keep the whole routing state in Redux but they have other drawbacks (for example, React Router state is unserializable which is contrary to how Redux works). So I think the suggestion I wrote above should solve your use case just fine.
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