添加redux
拢共分几步?
- 拢共分四步
- 新建
action
- 新建
reducer
- 新建
store
- 使用
provider
包裹
新建action
- 通常来说,
action
需要返回一个通用的格式的数据,可以根据自己业务体量的不同,定义不同的数据结构,我这边是这样定义的
// 同步的action
const test = (option) => {
const { data } = option;
return {
type: 'TEST',
payload: {
data
}
}
}
// 异步的action,通常来说,异步的action一般是伴随着ajax请求的,所以考虑在store创建的时候添加一个promise中间件来专门处理异步的action
const testAsync = (option) => {
const { params, success, error, data } = option;
return {
type: 'TEST_ASYNC',
payload: {
promise: axios({ type: 'post', params, data }), // api层可以根据自己的业务进行一层封装,这个地方就举个例子,可以在这里地方把params和data传入axios,也可以在中间件中区处理
success,
error
}
}
}
新建reducer
- 官网上的
reduce
r是通过switch
来进行判断的,感觉这样的写法,每次要写分支,而且还不能漏掉一个break
,一旦漏掉一个break
,就会出现很奇怪的错误。所以我将reducer
进行一层简单的包裹
/**
* 对所有的reducer做一层封装
* @param {初始化的state}} initialState
* @param {初始化的调用action的方法} handlers
*/
const createReducer = (initialState, handlers) => {
return (state, action) => {
let _state = initialState;
if (state !== undefined) {
_state = state;
}
const handler = action && action.type ? handlers[action.type] : undefined;
if (!handler) {
return _state;
}
return handler(_state, action);
};
};
/...reducer.../
const initData = {
count: 0, // 计数器
}
const handle = {
'TEST_ADD': (state, action) => ({ ...state, count: state.count += action.payload.data }),
}
export default createReducer(initData, handle);
创建store
- 通常创建
store
的时候,只需要调用createStore
就可以了,但是,针对一些特殊的业务需求,或者是需要一些定制化的功能,可以考虑在创建store
的时候添加中间件,来做一些相关的处理
import { createStore, combineReducers, applyMiddleware } from "redux";
import index from 'Reducers';
import { promiseMiddleware, LoggerMiddleware } from 'Utils/reduxUtil';
// 混合多个reducer,当有多个时候,直接使用这个方法进行混合
const reducers = combineReducers({
index
});
const store = createStore(
reducers,
applyMiddleware(
promiseMiddleware,
LoggerMiddleware
)
);
export default store;
/...中间件,写的比较简单,可以自己根据自己需要去编写.../
/**
* 自定义promise中间件,对异步的action作处理,action统一只传type和payload两个值,payload为action的除了type的所有参数集合
* 需要后端返回的数据格式保持一致,统一为{ data, status, msg },
* 需要返回一个带getMe的json,getMe为此次promise请求的结果,
* 如果返回的数据不是data也可以,在配置action时传一个customField的字段告诉中间件其返回的字段名称
* 同时可以不设置type,直接把action当做一个普通的api请求,保持整体的调用api都是通过action来调用,风格一致。
*/
const promiseMiddleware = store => next => action => {
const { payload: { params, promise, success, error, customField } } = action;
if (!promise) {
return next(action);
}
return promise(params).then((res) => {
action.payload.data = res;
success && success(res);
next(action);
}).catch(err => {
error && error(err);
});
}
const LoggerMiddleware = store => next => action => {
const { type, payload } = action;
console.log('-------------------------------------');
console.log(`type = ${type}`);
console.log(`payload = `, payload);
return next(action);
};
最后,渲染时在最外层使用Provider
去包裹,便可以直接使用了.
- 没啥好注意的,只要前面的没啥问题,到这一步就是加行代码
import React from "react";
import ReactDOM from "react-dom";
import { Router } from 'react-router';
import { Provider } from 'react-redux';
import { createBrowserHistory } from 'history';
import { renderRoutes } from 'Utils/component';
import routes from 'Routes';
import store from 'Store';
console.log(store.getState())
const browserHistory = createBrowserHistory();
function render() {
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
{
renderRoutes(routes)
}
</Router>
</Provider>,
document.getElementById("root")
);
}
render();
在组件里面使用
- 在此,我一般是用
connect
结合redux
的bindActionCreators
来使用的
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as indexAction from 'Actions';
import { AsyncComponent } from 'Utils/component';
const HelloWorld = AsyncComponent(() => import('Components/test/HelloWorld'));
class Test extends Component {
add = () => {
const { indexAction: { testAdd } } = this.props;
testAdd({ data: Number(this.input.value) });
}
render() {
const { count } = this.props;
return (
<div className="Test">
<HelloWorld />
<input type="number" ref={input => this.input = input} />
<div className="add" onClick={this.add}>增加</div>
<div className="show">{count}</div>
</div>
)
}
}
const mapStateToProps = state => {
const { index } = state;
return {
...index
};
}
const mapDispatchToProps = dispatch => {
return {
indexAction: bindActionCreators(indexAction, dispatch),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps,
null,
{ forwardRef: true }
)(Test);
小结
- 至此,所有的步骤已经结束,
redux
也完整的加入到了框架里。其实,redux
还可以和react-router
结合起来使用,当有场景需要使用时,推荐使用connected-react-router
。
- 一个包含
react
所有基本功能的空框架也搭建完毕,整体来说,基本功能都是具备了,可能就是需要针对不同业务来进行进一步的设计。主要是为了写下来,以后自己方便复习。
- 完整的代码示例,可以访问我的
github
地址,https://github.com/810307015/ReactDemo
- 后面想写点除了框架以外的东西,或者是框架本身内部的实现。