06. Navigating with React Router <Link>

Video Link

In this lesson we will update the "links" that control the visibility filter to behave more like real links. We're going to change it so our browser's "Back" button works, and the URL changes when we switch filters.

We'll start by adding a parameter to the Route path called filter. We wrap it in parens to tell React Router that it's optional (we want all Todos shown by default).

Update Root.js

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

We also need to update our visibility filter links inside of the footer.

Footer.js Before

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

export default Footer;

The previous implementation used the convention for the filter prop, but we will update them to align with the "active" and "completed" paths we want displayed in the address bar.

Footer.js After

// Rest as above...
<p>
  Show:
  {" "}
  <FilterLink filter="all">
    All
  </FilterLink>
  {", "}
  <FilterLink filter="active">
    Active
  </FilterLink>
  {", "}
  <FilterLink filter="completed">
    Completed
  </FilterLink>
</p>

We use a null string to signify the default path and avoid passing an empty string.

Update FilterLink.js Implementation

In our current implementation, the FilterLink component dispatches an action every time that it's clicked, then reads its active state from the store and compares its filter prop to the visibilityFilter in the store.

FilterLink.js Before

...
const mapStateToProps = (state, ownProps) => ({
  active: ownProps.filter === state.visibilityFilter,
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  onClick() {
    dispatch(setVisibilityFilter(ownProps.filter));
  },
});

const FilterLink = connect(
  mapStateToProps,
  mapDispatchToProps
)(Link);

export default FilterLink;

However, we no longer need this implementation since we want the router to be in control of any state that is in the URL. We'll import Link from react-router and use it in our new implementation.

FilterLink now accepts filter as a prop, and render it through React Router's Link.

The to prop corresponds to path we want the link to point to, so if the filter is 'all', we're going to use the root path, otherwise we will use filter itself as the URL's path.

We also will use the activeStyle prop to style it differently when its to prop matches the current path.

We pass children to the Link itself, and add children as a prop to FilterLink so that the parent component can specify the children.

FilterLink.js After

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

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

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

export default FilterLink;

More Cleanup to Do...

We are no longer using the setVisibilityFilter action creator, so it can be removed from src/actions/index.js, leaving us with just addTodo and toggleTodo action creators.

We can also delete our custom Link component from src/components since we are now using the one provided by react-router.

Recap at 2:20 in video

results matching ""

    No results matching ""