Redux: Navigating with React Router <Link>

Dan Abramov
InstructorDan Abramov

Share this video with your friends

Send Tweet
Published 6 years ago
Updated 3 years ago

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

[00:00] The links that control the visibility filter 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 visibility filter.

[00:16] I will add a parameter to my route called filter, and I need to wrap it in parens to tell React Router that it's optional, because if it's not specified, I want to show all todos.

[00:28] 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.

[00:52] My current implementation of the filter link component dispatches an action every time it's clicked, and reads its active state from the store, comparing its filter prop to the visibility filter in the store.

[01:06] 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 filter link component is going to use it.

[01:24] 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.

[01:43] 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 filter link so that the parent component can specify the children.

[02:02] Now I'm exporting filter link, and I have some cleanup to do. I can remove the set visibility filter 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.

[02:19] 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.

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

kcrossfitter
kcrossfitter
~ 5 years ago

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?

Brian
Brian
~ 5 years ago

+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.

Robert Smith
Robert Smith
~ 5 years ago

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;
Andre
Andre
~ 5 years ago

How does the application know which todo's to render when the getVisibleTodos reducer in VisibleTodoList.js still has values with actions: SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE yet in footer.js we're passing filter params of all, completed, and active to <FilterLink filter=<all,completed,active>...., there seems to be some kind of disconnect here?

Sean Cooper
Sean Cooper
~ 4 years ago

For React Router v4 I have the following:

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

Not sure if that's correct but it does what the video does.

Sean Cooper
Sean Cooper
~ 4 years ago
Alex Okros
Alex Okros
~ 4 years ago

For Router v4, see this:

https://stackoverflow.com/questions/35604617/react-router-with-optional-path-parameter

Also:

Root.js

import React from 'react';
import { Provider } from 'react-redux';
import { browserHistory } from 'react-router';
import { BrowserRouter, Route } from 'react-router-dom';
import App from './App';

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

export default Root;

FilterLink.js

import React, { PropTypes } from 'react';
import { NavLink } from 'react-router-dom';

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

FilterLink.propTypes = {
  filter: PropTypes.oneOf(['all', 'completed', 'active']).isRequired,
  children: PropTypes.node.isRequired,
};

export default FilterLink;
J. Matthew
J. Matthew
~ 3 years ago

How does the application know which todo's to render when the getVisibleTodos reducer in VisibleTodoList.js still has values with actions: SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE yet in footer.js we're passing filter params of all, completed, and active to <FilterLink filter=<all,completed,active>...., there seems to be some kind of disconnect here?

That's covered in the next video.