从无到有-在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-router、react-router-dom和react-router-native。
react-router实现了路由的核心的路由组件和函数。而react-router-dom和react-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.js、WechatLayout.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。
参考
- https://github.com/mrdulin/blog/issues/42
- https://cn.redux.js.org/docs/recipes/reducers/UsingCombineReducers.html
项目源代码
从无到有-在create-react-app基础上接入react-router、redux-saga的更多相关文章
- 诱人的 react 视频教程-基础篇(14 个视频)
诱人的 react 视频教程-基础篇(14 个视频) 诱人的 react 视频教程 - 基础篇 #1 介绍「07:25」 诱人的 react 视频教程 - 基础篇 #2 create-react-ap ...
- 深入 Create React App 核心概念
本文差点难产而死.因为总结的过程中,多次怀疑本文是对官方文档的直接翻译和简单诺列:同时官方文档很全面,全范围的介绍无疑加深了写作的心智负担.但在最终的梳理中,发现走出了一条与众不同的路,于是坚持分享出 ...
- 在 .NET Core 5 中集成 Create React app
翻译自 Camilo Reyes 2021年2月22日的文章 <Integrate Create React app with .NET Core 5> [1] Camilo Reyes ...
- Hybrid APP基础篇(二)->Native、Hybrid、React Native、Web App方案的分析比较
说明 Native.Hybrid.React.Web App方案的分析比较 目录 前言 参考来源 前置技术要求 楔子 几种APP开发模式 概述 Native App Web App Hybrid Ap ...
- 如何扩展 Create React App 的 Webpack 配置
如何扩展 Create React App 的 Webpack 配置 原文地址https://zhaozhiming.github.io/blog/2018/01/08/create-react-a ...
- tap news:week5 0.0 create react app
参考https://blog.csdn.net/qtfying/article/details/78665664 先创建文件夹 安装create react app 这个脚手架(facebook官方提 ...
- Create React App
Facebook开源了React前端框架(MIT Licence),也同时提供了React脚手架 - create-react-app. create-react-app遵循约定优于配置(Coc)的原 ...
- 使用create react app教程
This project was bootstrapped with Create React App. Below you will find some information on how to ...
- [React] Create & Deploy a Universal React App using Zeit Next
In this lesson, we'll use next to create a universal React application with no configuration. We'll ...
随机推荐
- JavaScript处理null、undefined和空值
最近一直在写前端,在使用jquery的ajax请求然后处理返回值的时候,经常会遇到返回值为undefined或者"null"的情况,很是头疼,而返回接口是来自于其他公司的,所以这种 ...
- pyenv global 设置失败 pyenv local 设置就成功了 不知道啥原因
dev@PC-20190309QPVT:/mnt/c/data/htdocs/python/flaskr$ pyenv global 3.6.1dev@PC-20190309QPVT:/mnt/c/d ...
- TimesTen数据库表中显示中文乱码的真正原因
上一篇博客TimesTen中文乱码问题(其实是cmd.exe中文乱码)的内容可能不对,也许只是个巧合?不得而知了.因为我今天重装系统了,把win10换成了win7(64bit).又安装了timeste ...
- Hive管理表,外部表及外部分区表的深入探讨
Hive管理表,也叫内部表.Hive控制着管理表的整个生命周期,默认情况下Hive管理表的数据存放在hive的主目录:/user/hive/warehouse/下,并且当我们删除一张表时,这张表的数据 ...
- 移动UI框架
---恢复内容开始--- 一,框架使用selenium+appium+po+unittest+python 1.其中po表示居于page of boject的思想,unittest是单元测试框架 2. ...
- 自定义类在PropertyGrid上的展示方法
自定义类在PropertyGrid上的展示方法 零.引言 PropertyGrid用来显示某一对象的属性,但是并不是所有的属性都能编辑,基本数据类型(int, double等)和.Net一些封装的类型 ...
- 你不知道的JS之作用域和闭包(三)函数 vs. 块级作用域
原文:你不知道的js系列 在第(二)节中提到的,标识符在作用域中声明,这些作用域就像是一个容器,一个嵌套一个,这个嵌套关系是在代码编写时定义的. 那么到底是什么产生了一个新的作用域,只有函数能做到 ...
- swust oj 1052
输出利用先序遍历创建的二叉树中的指定结点的双亲结点 1000(ms) 10000(kb) 2415 / 5575 利用先序递归遍历算法创建二叉树并输出该二叉树中指定结点的双亲结点.约定二叉树结点数据为 ...
- 第三次作业(ABC类代码优化及感悟)
这次作业与林杰同学合作完成,不重复发布了. 可以直接去林杰同学的博客看.http://www.cnblogs.com/linlinlin/p/4836707.html
- Vue 学习笔记 — 组件初始化
简书 在vue中有3个概念很容易搞混,data,computed,props,特别是我们这些原后端开发人员. new Vue({ el: "#x", data: { id: 1 } ...