Enter Your Email Address to Watch This Lesson

Your link to unlock this lesson will be sent to this email address.

Unlock this lesson and all 1046 of the free egghead.io lessons, plus get JavaScript content delivered directly to your inbox!



Existing egghead members will not see this. Sign in.

Redux: Navigating with React Router <Link>

2:38 JavaScript lesson by

We will learn how to change the address bar using a component from React Router.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

We will learn how to change the address bar using a component from React Router.

Avatar
kcrossfitter

Dan, thanks for the great course!

I'm following this course with create-react-app cli.
Before this lesson, everything was fine!
But with react-router version 3.0.2 it's not working, I couldn't re-select 'All' filter.
So I uninstalled react-router 3.0.2 and re-installed version 2.4.0 as in the original package.json. It works but throws a lot of warning saying

Warning: You are manually calling a React.PropTypes validation function for the path prop on Route. This is deprecated and will not work in production with the next major version. You may be seeing this warning due to a third-party PropTypes library. See https://fb.me/react-warning-dont-call-proptypes for details.

I changed to version 2.8.1.
Then it's perfectly fine.
Could you explain what part of the code need to be modified to use react-router version 3?

Avatar
Brian

+1 I've run into the same hiccup with the Link not routing to the index/'all' filter when passed an empty string (also using v3.0.2). Clicking 'All' yields no response. Flawless course otherwise; excellent explanations! Thank you, Dan.

In reply to kcrossfitter
Avatar
Robert Smith

For React Router v3 you need to use the IndexLink component as it's to prop equals the root path.
https://github.com/ReactTraining/react-router/blob/v3/docs/guides/IndexRoutes.md#index-links

This is the code I went with in this instance:

import React from 'react';
import { Link, IndexLink } from 'react-router';

const FilterLink = ({ filter, children }) => {
  const style = {
    textDecoration: 'none',
    color: 'black',
  };

  return filter === 'all'
    ? <IndexLink to="/" activeStyle={style}>{children}</IndexLink>
    : <Link to={filter} activeStyle={style}>{children}</Link>;
};

export default FilterLink;
In reply to Brian

The links that control the visibilityFilter do not currently behave like real links. I'd like to change it so that the background works and the current URL updates when I click on these links and change the current visibilityFilter.

[output](../images/javascript-redux-navigating-with-react-router-link-output.png)

I will add a parameter to my Route called filter, and I need to wrap it in parenthesis to tell react-router that it's optional, because if it's not specified, I want to show all todos.

index.js

const Root = ({ store }) => (
  <Provider store={store}>
    <Router history={browserHistory}>
      <Route path='/(:filter)' component={App} />
    </Router>
  </Provider>
);

Now I'm opening the Footer component that displays the links. Right now, I'm using the custom convention for the filter prop, but I'm going to change this to align more closely with the paths I want to be displayed, so I'm using active and completed for the active and completed paths, and to avoid passing an empty string, I'll just use a null string to signify the default path.

Footer.js

const Footer = () => (
  <p>
    Show:
    {" "}
    <FilterLink filter="SHOW_ALL">
      All
    </FilterLink>
    {", "}
    <FilterLink filter="SHOW_ACTIVE">
      Active
    </FilterLink>
    {", "}
    <FilterLink filter="SHOW_COMPLETED">
      Completed
    </FilterLink>
  </p>
);

My current implementation of the filterLink component dispatches an action every time it's clicked, and reads its active state from the store, comparing its filter prop to the visibilityFilter in the store.

However, I'm not going to need this implementation anymore because I want the Router to be in control of any state that is in the URL. This is why I import link from react-router, and my new implementation of the FilterLink component is going to use it.

FilterLink.js

import React from 'react';
import { Link } from 'react-router';

const FilterLink = ()

It accepts filter as a prop, and it renders through the link provided by react-router. Its true prop corresponds to the path that we want the link to point to, so if the filter is all we're going to use the root path. Otherwise, we'll just use the filter itself as the path.

FilterLink.js

import React from 'react';
import { Link } from 'react-router';

const FilterLink = ({ filter }) => (
  <Link
    to={filter === 'all' ? '' : filter}

);

I am also specifying the active style prop so that the link is styled differently when its true prop matches the current path. Finally, I'm passing children to the link itself, and I'm adding children as a prop to FilterLink so that the parent component can specify the children.

FilterLink.js

import React from 'react';
import { Link } from 'react-router';

const FilterLink = ({ filter }) => (
  <Link
    to={filter === 'all' ? '' : filter}
    activeStyle={{
      textDecoration: 'none',
      color: 'black',
    }}
  >
    {children}
  </Link>
);

export default FilterLink;

Now I'm exporting filter link, and I have some cleanup to do. I can remove the setVisibilityFilter action-creator, as I don't use it anymore. I can also remove my custom link component, because now I'm using the link from react-router instead of it.

If I run the app now, clicking on the link will update the URL bar. It also works in the other direction. Clicking on the back or forward buttons gets the corresponding link active.

[output](../images/javascript-redux-navigating-with-react-router-link-output2.png)

In the next lesson, we will teach our component to read the current filter from the URL instead of Redux store.

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?