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 parents to tell React Router that it's optional (we want all Todos shown by default).
const Root = ({ store }) => (
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/(:filter)" component={App} />
</Router>
</Provider>
);
.
.
.
Note: the explanations in the video require a version of react-router
previous to the 4.0.0. Starting in that version some changes have been included which require this slightly different syntax:
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.
...
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.
// 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.
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.
...
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.
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;
Note: the explanations in the video require a version of react-router
previous to the 4.0.0. Starting in that version some changes have been included which require this slightly different syntax:
import React, { PropTypes } from 'react';
import { NavLink } from 'react-router-dom';
const FilterLink = ({ filter, children }) => (
<NavLink
exact
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;
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
.