从无到有-在create-react-app基础上接入react-router、redux-saga

搭建项目框架

新建项目

实行以下代码,用create-react-app来竖立项目的基本框架,然后装置须要用到的依靠。

$ npx create-react-app my-test-project
$ cd my-test-project
$ yarn add react-router-dom react-redux prop-types redux redux-saga
$ yarn start

完成后,运用启动在localhost的3000端口。

接入react-router-dom

react-router-dom实在就是react-router 4.0,与之前的3.0有什么区别呢?react-router被一分为三。react-routerreact-router-domreact-router-native

react-router完成了路由的中心的路由组件和函数。而react-router-domreact-router-native则是基于react-router,供应了特定的环境的组件。

react-router-dom依靠react-router,装置的时刻,不必再显现的装置react-router, 假如你有时机去看react-router-dom的源码,就会发明内里有些组件都是从react-router中引入的。

新建layout

/src下新建layout目次。为何要新建layout目次,由于有能够我们会用到多个layout,layout是一个什么样的观点?

比方这个运用须要供应一部分功能在微信运用。那末进入一切微信的相干界面下都要举行鉴权。没有鉴权信息就不许可接见,然则这个效劳依然有一切人都能够接见的路由。运用layout能够很好的帮我们处理这个题目。

将一切的须要鉴权的页面放在比方WechatContainer下,只要在有微信相干鉴权的信息存在,才许可接见接下来的界面,不然,容器内以至能够直接不衬着接下来的界面。

/src/layout下新建两个文件,分别是AppLayout.jsWechatLayout.js

AppLayout.js的代码以下。在这个layout中,首页就是纯真的一个路由,导向至首页。而接下来的/wechat则是把路由导向至了一个微信端专用的layout。

import React, { Component } from 'react';
import Home from '../routes/home';
import WechatLayout from './WechatLayout';
import { Route, Switch } from 'react-router-dom';

/**
 * 项目进口规划
 * 在此处依据一级路由的差别进入差别的container
 * 每一个container有本身差别的作用
 *
 * 在react-router V4中,将本来统一在一处的路由疏散到各个模块中,疏散到各个模块当中
 * 比方: WechatLayout的路由为/wechat 示意到该layout下的默许途径
 */
class AppLayout extends Component {
  constructor(props) {
    super(props);

    this.state = {};
  }

  render() {
    return (
      <div className='App'>
        <main>
          <Switch>
            <Route path='/' exact component={Home} />
            <Route path='/wechat' component={WechatLayout} />
          </Switch>
        </main>
      </div>
    );
  }
}

export default AppLayout;

WechatLayout.js的代码以下。在这个layout中,我们就能够对接见该路由的用户举行鉴权。假如没有权限,我们能够直接限定用户的接见,以至直接不衬着render中的数据。

比方,我们能够在componentWillMount中或许在render中,依据当前的state数据,对当前用户举行鉴权。假如没有权限,我们就能够将当前页面重定向到没有权限的提醒界面。

import React, { Component } from 'react';
import Home from '../routes/wechat/home';
import { Route, Switch } from 'react-router-dom';
import { connect } from 'react-redux';

class WechatLayout extends Component {
  constructor(props) {
    super(props);

    this.state = {};
  }

  componentWillMount() {
  }

  render() {
    const className = 'Wechat-Layout';

    return (
      <div className={`${className}`}>
        <header>
          Our Manage Layout
        </header>
        <main>
          <Switch>
            <Route path={`${this.props.match.path}/home`} component={Home} />
          </Switch>
        </main>
      </div>
    );
  }
}

const mapStateToProps = state => ({
  reducer: state.wechatLayout
});

export default connect(mapStateToProps)(WechatLayout);

新建routes

新建/src/routes/home/index.js,代码以下。

import React, { Component } from 'react';
import {Link} from 'react-router-dom';

class Home extends Component {
  constructor(props) {
    super(props);

    this.state = {};
  }

  render() {
    const className = 'Home';

    return (
      <div className={`${className}`}>
        <h1>This is Home</h1>
        <div><Link to={'/wechat/home'}>Manage Home</Link></div>
      </div>
    );
  }
}

export default Home;

新建/src/routes/wechat/home/index.js, 代码以下。在代码中能够看到,触发reducer很简朴,只须要挪用dispatch要领即可。dispatch中的payload就是该要求所带的参数,该参数会传到saga中间层,去挪用真正的后端要求。并在要求返回胜利以后,挪用put要领更新state。

import React, { Component } from 'react';
import {connect} from "react-redux";

class Home extends Component {
  constructor(props) {
    super(props);

    this.state = {};
  }

  componentWillMount() {
    this.props.dispatch({ type: 'WATCH_GET_PROJECT', payload: { projectName: 'tap4fun' } });
  }

  render() {
    const className = 'Wechat-Home';

    return (
      <div className={`${className}`}>
        <h1>Home</h1>
        <h2>The project name is : { this.props.reducer.projectName }</h2>
      </div>
    );
  }
}

const mapStateToProps = state => ({
  reducer: state.wechat
});

export default connect(mapStateToProps)(Home)

新建container

/src下新建container,在container中新建文件AppContainer.js。我们全部react运用都装在这个容器内里。AppContainer.js的代码以下。

而个中的Provider组件,将包裹我们运用的容器AppLayout包在个中,使得下面的一切子组件都能够拿到state。Provider接收store参数作为props,然后经由过程context往下通报。

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import AppLayout from '../layout/AppLayout';

class AppContainer extends Component {
  constructor(props) {
    super(props);

    this.state = {};
  }

  static propTypes = {
    store: PropTypes.object.isRequired
  };

  render() {
    const { store } = this.props;

    return (
      <Provider store={store}>
        <Router>
          <AppLayout />
        </Router>
      </Provider>
    );
  }
}

export default AppContainer;

修正项目进口文件

更新/src/index.js,代码以下。在此处会将create出来的store容器看成属性传入到Appcontainer中,作为我们运用的状况容器。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import AppContainer from './container/AppContainer';
import createStore from './store/createStore';

const store = createStore();

ReactDOM.render(<AppContainer store={store} />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();

新建store

新建/src/store/craeteStore.js,代码以下。经由过程以下的体式格局,我们能够给redux增加许多中间件,以至是本身写的中间件。

比方,我们能够本身完成一个日记中间件,然后增加到中间件数组middleWares中,在建立redux的store的时刻,运用我们本身写的中间件。

import { applyMiddleware, compose, createStore } from 'redux';
import createSagaMiddleware  from 'redux-saga';
import rootReducer from '../reducers';
import rootSaga  from '../saga';

export default function configureStore(preloadedState) {
  // 建立saga中间件
  const sagaMiddleware = createSagaMiddleware();
  const middleWares = [sagaMiddleware];
  const middlewareEnhancer = applyMiddleware(...middleWares);

  const enhancers = [middlewareEnhancer];
  const composedEnhancers = compose(...enhancers);

  // 建立存储容器
  const store = createStore(rootReducer, preloadedState, composedEnhancers);
  sagaMiddleware.run(rootSaga);

  return store;
}

在这引入了redux-saga。我之前在运用redux的时刻,几乎在每一个模块都要写响应的action和reducer,然后在响应的模块文件中引入action的函数,然后在运用mapDispatchToProps将该函数注入到props中,在响应的函数中挪用。而且,一个action不能复用,纵然触发的是雷同的reducer。如许就会涌现许多重复性的代码,新增一个模块的事情也相对烦琐了许多。

然则运用了redux-saga以后,只须要在reducer中定义好响应范例的操作和saga就能够了。不须要定义action的函数,不须要在文件中引入action中函数,以至连mapDispatchToProps都不须要,直接运用this.props.dispatch({ 'type': 'WATCH_GET_PROJECT' })就能够挪用。而且,action能够复用。

新建saga

新建/src/saga/index.js,代码以下。

import { put, takeEvery } from 'redux-saga/effects';
import { delay } from 'redux-saga';

export function* fetchProject() {
  yield delay(1000);
  yield put({ type: 'GET_PROJECT' })
}

export default function * rootSaga() {
  yield takeEvery('WATCH_GET_PROJECT', fetchProject);
}

新建reducer

新建/src/reducers/wechat.js,代码以下。

const initialState = {
  projectName: null
};

export default function counter(state = initialState, action) {
  let newState = state;
  switch (action.type) {
    case 'GET_PROJECT':
      newState.projectName = action.payload.projectName;
      break;
    default:
      break;
  }
  return {...newState}
}

新建/src/reducers/index.js,代码以下。

import { combineReducers } from 'redux';
import Wechat from './wechat';

export default combineReducers({
  wechat: Wechat
});

在这里我们运用了combineReducers。在之前的基于redux的运用程序中,罕见的state构造就是一个简朴的JavaScript对象。

重新启动运用

到此处,重新启动运用,就能够在http://localhost:3000/wechat/home下看到从reducer中掏出的数据。

在页面中,我们就能够经由过程代码this.props.dispatch的体式格局,来触发action。

参考

项目源代码

Github堆栈

    原文作者:detectiveHLH
    原文地址: https://segmentfault.com/a/1190000017394555
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞