fullstackReact 学习笔记 Routing(三)
示例代码及相关知识点
1、App.js
import React from 'react';
import { Route, Redirect, Switch } from 'react-router-dom';
import TopBar from './TopBar';
import PrivateRoute from './PrivateRoute';
import AlbumsContainer from './AlbumsContainer';
import Login from './Login';
import Logout from './Logout';
import '../styles/App.css';
const NoMatch = ({ location }) => (
<div className='ui inverted red raised very padded text container segment'>
<strong>Error!</strong> No route found matching:
<div className='ui inverted black segment'>
<code>{location.pathname}</code>
</div>
</div>
);
const App = () => (
<div className='ui grid'>
<TopBar />
<div className='spacer row' />
<div className='row'>
<Switch>
<PrivateRoute path='/albums' component={AlbumsContainer} />
<Route path='/login' component={Login} />
<Route path='/logout' component={Logout} />
<Route exact path='/' render={() => (
<Redirect
to='/albums'
/>
)} />
<Route component={NoMatch} />
</Switch>
</div>
</div>
);
export default App;
<PrivateRoute >主要提供的是需要验证获取token后才能看到的组件。
2、PrivateRoute.js
/* eslint-disable no-shadow */
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import { client } from '../Client';
const PrivateRoute = ({ component, ...rest }) => (
<Route {...rest} render={(props) => (
client.isLoggedIn() ? (
React.createElement(component, props)
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location },
}} />
)
)} />
);
export default PrivateRoute;
{component ,...rest}用于提取传递给PrivateRoute的props;rest相当于{path:'/albums'}
如果客户已经登录的话,那么返回对应的组件,否则的话重定向到登录界面。
state.from=props.location用于记录用户从哪个页面过来,以便于登录后还返回该页面。向Redirect传递props.state=当前页面的location,可以通过location.pathname获取当前页面的路径。
3、Login.js
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import { client } from '../Client';
class Login extends Component {
state = {
loginInProgress: false,
shouldRedirect: false,
};
performLogin = () => {
this.setState({ loginInProgress: true });
client.login().then(() => (
this.setState({ shouldRedirect: true })
));
};
redirectPath = () => {
const locationState = this.props.location.state;
const pathname = (
locationState && locationState.from && locationState.from.pathname
);
return pathname || '/albums';
};
render() {
if (this.state.shouldRedirect) {
return (
<Redirect to={this.redirectPath()} />
);
} else {
return (
<div className='ui one column centered grid'>
<div className='ten wide column'>
<div
className='ui raised very padded text container segment'
style={{ textAlign: 'center' }}
>
<h2 className='ui green header'>
Fullstack Music
</h2>
{
this.state.loginInProgress ? (
<div className='ui active centered inline loader' />
) : (
<div
className='ui fluid large green submit button'
onClick={this.performLogin}
>
Login
</div>
)
}
</div>
</div>
</div>
);
}
}
}
export default Login;
redirectPath用于计算从哪个路径过来的,以便于返回该路径。
4、Logout.js
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import { client } from '../Client';
class Logout extends Component {
constructor(props) {
super(props);
client.logout();
}
render() {
return (
<Redirect
to='/login'
/>
);
}
}
export default Logout;
5、AlbumsContainer.js
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import Album from './Album';
import VerticalMenu from './VerticalMenu';
import { client } from '../Client';
const ALBUM_IDS = [
'23O4F21GDWiGd33tFN3ZgI',
'3AQgdwMNCiN7awXch5fAaG',
'1kmyirVya5fRxdjsPFDM05',
'6ymZBbRSmzAvoSGmwAFoxm',
'4Mw9Gcu1LT7JaipXdwrq1Q',
];
class AlbumsContainer extends Component {
state = {
fetched: false,
albums: [],
};
componentDidMount() {
this.getAlbums();
}
getAlbums = () => {
client.getAlbums(ALBUM_IDS)
.then((albums) => (
this.setState({
fetched: true,
albums: albums,
})
));
};
render() {
if (!this.state.fetched) {
return (
<div className='ui active centered inline loader' />
);
} else {
const matchPath = this.props.match.path;
return (
<div className='ui two column divided grid'>
<div
className='ui six wide column'
style={{ maxWidth: 250 }}
>
<VerticalMenu
albums={this.state.albums}
albumsPathname={matchPath}
/>
</div>
<div className='ui ten wide column'>
<Route exact path={matchPath} render={() => (
<div>
<h3>Please select an album on the left</h3>
</div>
)} />
<Route
path={`${matchPath}/:albumId`}
render={({ match }) => {
const album = this.state.albums.find(
(a) => a.id === match.params.albumId
);
return (
<Album
album={album}
albumsPathname={matchPath}
/>
);
}}
/>
</div>
</div>
);
}
}
}
export default AlbumsContainer;
6、Album.js
唱片的具体信息:
import React from 'react';
import { Link } from 'react-router-dom';
import '../styles/Album.css';
import { durationToHuman } from '../Helpers';
const Album = ({ album, albumsPathname }) => (
<div className='Album'>
<div className='row'>
<div className='ui middle aligned three column grid'>
<div className='six wide column' style={{ minWidth: '212px' }}>
<img
src={album.imageUrl}
style={{ width: '212px' }}
alt='album'
/>
</div>
<div className='one wide column' />
<div className='six wide column'>
<p>
{
`By ${album.artist.name}
- ${album.year}
- ${album.tracks.length} songs`
}
</p>
<Link
to={albumsPathname}
className='ui left floated large button'
>
Close
</Link>
</div>
</div>
</div>
<div className='spacer row' />
<div className='row'>
<table
className='ui very basic single line unstackable selectable table'
>
<thead>
<tr>
<th>#</th>
<th>Song</th>
<th><i className='icon clock' /></th>
</tr>
</thead>
<tbody>
{
album.tracks.map((track) => (
<tr
key={track.id}
>
<td>{track.trackNumber}</td>
<td>{track.name}</td>
<td>
{durationToHuman(track.durationMs)}
</td>
</tr>
))
}
</tbody>
</table>
</div>
</div>
);
export default Album;
8、VerticalMenu.js
边框导航栏
import React from 'react';
import { NavLink } from 'react-router-dom';
import '../styles/VerticalMenu.css';
const VerticalMenu = ({ albums, albumsPathname }) => (
<div className='ui secondary vertical menu'>
<div className='header item'>
Albums
</div>
{
albums.map((album) => (
<NavLink
to={`${albumsPathname}/${album.id}`}
activeClassName='active'
className='item'
key={album.id}
>
{album.name}
</NavLink>
))
}
</div>
);
export default VerticalMenu;
NavLink用于高亮显示被选中的链接。相当于在Link的基础上附加了高亮显示的功能。对于上一级路径我们尽可能的采用参数传递的方式,以便于修改一级路径时,二级路径不用跟着修改。
9、ToolBar.js
import React from 'react';
import { Link } from 'react-router-dom';
import { client } from '../Client';
const TopBar = () => (
<div
className='ui huge top attached fluid secondary menu'
>
<div className='item' />
<div className='item'>
<h1
className='ui green header'
style={{ marginTop: '10px' }}
>
Fullstack Music
</h1>
</div>
<div className='right menu'>
{
client.isLoggedIn() ? (
<Link className='ui item' to='/logout'>
Logout
</Link>
) : (
<Link className='ui item' to='/login'>
Login
</Link>
)
}
</div>
</div>
);
export default TopBar;
是否是登录状态,如果已经登录则显示logout,如果没有登录,显示Login。
浙公网安备 33010602011771号