Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

With React Redux Router, how should I access the state of the route?

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.

like image 204
Jason D Avatar asked Feb 27 '16 04:02

Jason D


People also ask

How do I access state in react router?

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.

How do we access state from Redux?

STEP-1 import useStore first from react-redux and then getState() function is used to access store state.

How do I get the current route in my react router?

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 .


1 Answers

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:

ChatRoomMessageList

const mapDispatchToProps = (dispatch) => ({
  onSendMessage(text) {
    dispatch({ type: 'SEND_MESSAGE', where: 'CHAT_ROOM', text })
  }
})

const ChatRoomMessageList = connect(
  mapStateToProps,
  mapDispatchToProps
)(MessageList)

FeedMessageList

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.

like image 179
Dan Abramov Avatar answered Oct 15 '22 09:10

Dan Abramov