React Router Review Notes
This is how the code used to be structured:
export default class App extends Component {constructor (props) {
super(props);
}componentDidMount () {
const { onLoad } = this.props;
Promise
.all([
fetch('/api/albums').then(res => res.json()),
fetch('/api/artists').then(res => res.json())
])
.then(results => onLoad(...results));
}render () {
const { location } = this.props;return (
<div id="main" className="container-fluid">
<div className="col-xs-2">
<SidebarContainer />
</div>
<div className="col-xs-10">
{
(() => {
switch (location) {
case 'albums': return <AlbumsContainer />
case 'artists': return <ArtistsContainer />
case 'album': return <AlbumContainer />
case 'artist': return <ArtistContainer />
}
})()
}
</div>
<PlayerContainer />
</div>
);
}
}
We have an IFFY that selectively renders child elements (Albums/ArtistsContainer) based on state.location.
Pretty manual spiel. So each button that changes the view has an onClick handler
// from the Sidebar component
<a href="#" onClick={() => go('artists')}>ARTISTS</a>
Which if you look in the container, is defined as:
import { switchLocation } from '../action-creators/location';const mapDispatchToProps = dispatch => ({
go: loc => dispatch(switchLocation(loc))
});
Which comes from action-creators
import { SWITCH_LOCATION } from '../constants';export const switchLocation = location => ({
type: SWITCH_LOCATION,
location
});
Which is a simple action creator. Now check out what the reducer does with it:
export default function (state = 'albums', action) {
switch (action.type) {
case SWITCH_LOCATION: return action.location;
default: return state;
}
}//const rootReducer = combineReducers({
albums,
artists,
currentSong,
currentSongList,
isPlaying,
location,
progress,
selectedAlbum,
selectedArtist
});
The root reducer defines state to have a property ‘location’, which is dispatched (as per the Sidebar Container) in classic redux style.
This is where React Router comes in. Instead of having to change the view in this convoluted manner (think about it — you’re dispatching a change to location, which bubbles up to store and bubbles back down as a state change — the app.js component renders differentially based on state.location).
react-router comes in to make changing views a lot easier (and also makes it possible to put view and URL in sync). In the current implementation, every view is going to be www.example.com.
With react-router, we want the website to have different URLs depending on the view:
——
React Router
(docs)
The first thing to restructure when using react-router is your root reactDOM render.
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>
<Route path='/' component={AppContainer}>
<Route path='/main' component={mainView} />
<Route path='/contacts' component={contactsView}>
<Route path='/contactForm' component={contactForm} />
<Route path='/contactList' component={contactList} />
</Route>
</Route>
</Router>
</Provider>,
document.getElementById('app')
);
The way in which you structure the code should conform to their real parent-child relationships. Note that, contactForm and contactList are both children of contacts, but only one will be displayed (/contacts/contactForm, /contacts/contactList). You can only go to one route along the same depth.
And also. Don’t forget
{this.props.children}
In your render(){…JSX…} code.
—
How does Link work?
<Link to="/contacts">Contacts</Link>
Renders based on our router definitions.
This is cool:
——
Other technologies we played around with were thunk (asynchronous action creators):
Allows us to return functions instead of just objects to dispatch. This allows for more complex logic.