此项目是用Create React App启动的.
可用脚本
在项目目录中,可以运行:
npm start
以开发模式运行应用程序。
打开http://localhost:3000在浏览器中查看。
如果进行编辑,页面将重新加载。
您还将在控制台中看到任何eslint错误。
项目目录结构
|-- package.json
|-- public
| |-- favicon.ico
| |-- index.html
| `-- manifest.json
|-- src
| |-- components // 封装组件
| | `-- Button.js
| |-- modals // 存放不同的action
| | |-- reducerCaption.js
| | `-- reducerFontSize.js
| |-- routes // 视图展示层
| | |-- App.js
| | |-- Counter.js
| | |-- FontSize.js
| | `-- Summary.js
| |-- index.css
| |-- index.js
| |-- App.test.js
| |-- serviceWorker.js
| `-- store.js // store仓库
|-- README.md
|-- .gitignore
`-- yarn.lock
demo示例功能点
- 使用Redux进行状态管理;
- 使用combineReducers将多个reducer组合管理。
代码地址:
https://github.com/Zhonghua926/demo-redux master分支
功能实现
- 1.安装
create-react-app
npm install -g create-react-app
- 2.查看版本
create-react-app --version
// 我的版本是2.1.1
- 3.通过命令创建一个
demo
文件
create-react-app demo
- 4.删除
App.js
,App.css
,然后
// 新建文件
|-- |-- components // 封装组件
| | `-- Button.js
| |-- modals // 存放不同的action
| | |-- reducerCaption.js
| | `-- reducerFontSize.js
| `-- routes // 视图展示层
| |-- App.js
| |-- Counter.js
| |-- FontSize.js
| `-- Summary.js
`-- store.js // store仓库
// 修改App.test.js
import App from './App'; -- > import App from './routes/App';
- 5.存放公用的state,以及对于这个state的一系列操作。
// reducerCaption.js
// type类型
const INCREMENT = 'increment';
const DECREMENT = 'decrement';
// 初始化state数据
const initValues = {
'First': 0,
'Second': 10,
'Third': 20,
}
// action行为
export const increment = (counterCaption) => {
return {
type: INCREMENT,
counterCaption,
}
}
export const decrement = (counterCaption) => {
return {
type: DECREMENT,
counterCaption,
}
}
// reducer
export function reducerCaption(state = initValues, action) {
const { counterCaption } = action;
switch (action.type) {
case INCREMENT:
return { ...state, [counterCaption]: state[counterCaption] + 1};
case DECREMENT:
return { ...state, [counterCaption]: state[counterCaption] - 1};
default:
return state;
}
}
- 6.在展示的组件里通过store.getState()获取公共的state状态,在组件加载完成后设置监听器,订阅onChange函数(当store变化,自动执行onChange函数,改变state状态,重新渲染页面)
// Counter.js
import React, { Component } from 'react';
import { PropTypes } from 'prop-types';
import store from '../store';
import { increment, decrement } from '../modals/reducerCaption';
import Button from '../components/Button';
class Counter extends Component {
constructor(props) {
super(props);
this.state = this.getOwnState();
}
componentDidMount() {
store.subscribe(this.onChange); // 设置监听器
}
componentWillUnmount() {
store.unsubscribe(this.onChange); // 关闭监听器,防止内存泄漏
}
getOwnState = () => {
const { reducerCaption } = store.getState(); // 获取store的状态
return {
value: reducerCaption[this.props.caption]
}
}
onChange = () => {
this.setState(this.getOwnState())
}
// 发起一个+1的action
onIncrement = () => {
store.dispatch(increment(this.props.caption));
}
// 发起一个-1的action
onDecrement = () => {
store.dispatch(decrement(this.props.caption));
}
render() {
const value = this.state.value;
const { caption } = this.props;
return (
<div>
<Button onClick={this.onIncrement}>+</Button>
<Button onClick={this.onDecrement}>-</Button>
<span>{caption} count: {value}</span>
</div>
)
}
}
// 对传入的值进行类型检查: 值必须为string且不能为空
Counter.propTypes = {
caption: PropTypes.string.isRequired,
}
export default Counter;
- 7.当然我们不能将所有的
reducer
都放在一起,通过switch
方法判断会使得代码太臃肿了,且不易维护,所以将不同的reducer
写在不同的地方,通过combineReducers
函数将这些reducer
联合起来,再通过createStore
方法创建一个仓库,这样我们可以通过es6解构store.getState()获取对应的reducer返回的数据。
// store.js
import { createStore, combineReducers } from 'redux';
import { reducerCaption } from './modals/reducerCaption';
import { reducerFontSize } from './modals/renderFontSize';
const reducer = combineReducers({
reducerCaption,
reducerFontSize
});
const store = createStore(reducer);
export default store;
使用Redux-saga
- 1.代码地址 https://github.com/Zhonghua926/demo-redux redux-saga分支
- 2.
Redux
只支持同步操作,这里我使用Redux-saga
中间件,来加强store
,使得Redux
可以调用异步操作
// FontSize.js
render() {
const {size} = this.state;
return (
<div>
...
<Button onClick={() => {store.dispatch({type: BIGGER_ASYNC, size})}}>3秒后增加</Button>
<Button onClick={() => {store.dispatch({type: SMALLER_ASYNC, size})}}>3秒后减小</Button>
...
</div>
)
}
// reducerFontSize.js
import { put, call, takeLatest } from 'redux-saga/effects';
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
function* biggerAsync(action) {
const { size } = action;
yield call(delay, 3000);
yield put({ type: BIGGER, size});
}
function* smallerAsync(action) {
const { size } = action;
yield call(delay, 3000);
yield put({ type: SMALLER, size});
}
export function* asyncSaga() {
yield takeLatest(BIGGER_ASYNC, biggerAsync); // 只监听最新的请求,可以防止多次请求,在短时间频繁点击时,只执行一次。
yield takeLatest(SMALLER_ASYNC, smallerAsync);
}
// modals目录下新建rootSaga.js
import { all } from 'redux-saga/effects';
import { asyncSaga } from './reducerFontSize';
export default function* rootSaga() {
yield all([
asyncSaga(),
])
}
// store.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { reducerCaption } from './modals/reducerCaption';
import { reducerFontSize } from './modals/reducerFontSize';
import createSagaMiddleware from 'redux-saga';
import rootSaga from './modals/rootSaga';
const sagaMiddleware = createSagaMiddleware()
let middlewares = []
middlewares.push(sagaMiddleware)
const reducer = combineReducers({
reducerCaption,
reducerFontSize
});
const store = createStore(reducer, applyMiddleware(...middlewares));
sagaMiddleware.run(rootSaga);
export default store;
- 3.
rootSaga.js
将不同组件的异步操作集中在一起,然后通过sagaMiddleware.run()
一起执行
网友评论