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。

 

posted @ 2018-08-23 16:54  tutu_python  阅读(189)  评论(0)    收藏  举报