06. Navigating with React Router <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>
);
.
.
.
Update Links in Footer.js
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
.