一、redux的封装
1.通过redux-thunk中间件
Redux store 仅支持同步数据流。使用 thunk 等中间件可以帮助在 Redux 应用中实现异步性。可以将 thunk 看做 store 的 dispatch() 方法的封装器;我们可以使用 thunk action creator 派遣函数或 Promise,而不是返回 action 对象。
注意,没有 thunk 的话,默认地是同步派遣。也就是说,我们依然可以从 React 组件发出 API 调用(例如使用 componentDidMount() 生命周期方法发出这些请求),但是我们在 Redux 应用中难以实现以下两点:
- 可重用性(思考下合成)
- 可预测性,只有 action creator 可以是状态更新的单一数据源
要在应用中使用 thunk 中间件,请务必安装 redux-thunk 软件包:
npm install --save redux-thunk
2.安装好中间件之后创建index和reduce文件
//index.js
import { createStore, applyMiddleware, compose } from "redux";
import thunk from 'redux-thunk'
import cRducer from "./reducer";
// redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(cRducer, composeEnhancers(
applyMiddleware(thunk)
))
export default store
//reducer.js
import { combineReducers } from 'redux-immutable';
import { reducer as store_demo } from 'src/page/store_demo/store/index';
// 多个reducer合并
const cRducer = combineReducers({
store_demo:store_demo, //定义每个文件夹下的名称,方便获取
});
export default cRducer;
注:reduce文件中combineReducers用于将若干个reduce进行合并
3.公共的方法封装好之后,在每个页面使用的时候,需要创建创建state和dispatch,如下:
注:当然也可以全部都放在公共创建,这样做是为了更好的维护。
//创建类型
//actionType.js
export const CHANGE_NUM = 'CHANGE_NUM'
//页面文件夹下的reduce
import * as actionTypes from './actionType'
import { Map } from 'immutable'
// 使用Immutable管理redux中的state (修改的`state`不会修改原有数据结构, 而是返回修改后新的数据结构)
const defaultState = Map({
num:1
})
function reducer(state = defaultState, action) {
switch (action.type) {
case actionTypes.CHANGE_NUM:
return state.set('num',action.num+1)
default:
return state
}
}
export default reducer
- 还有一个对action的统一管理,这个可有可无,但是非常推荐使用。在页面使用不用再引入type,直接引入actionCreator编写好的函数,如下:
import * as actionType from './actionType'
export const changeMessageNum = (data) => {
return {
type: actionType.CHANGE_NUM,
data
};
};
二、使用
1.connect方式
这种方式是通过connect将action方法绑定到props中,使用时直接用props.xx,与传值的形式一样。
import { connect } from "react-redux";
import { changeMessageNum } from "store/Reducer/App/action";
import { bindActionCreators } from "redux";
export default connect(
(state) => ({
num: state.num
}),
dispatch => bindActionCreators({ changeMessageNum }, dispatch)
)(App);
这种方式写起来代码的重复非常高,每个页面都需要写。不过,react-redux 7.1之后可以使用useSelector、useDispatch等HooksApi替代connect,减少模板代码。
2.React Hooks方式
通过useDispatch, useSelector两个hook对引入state和action中的变量和方法,非常的方便。
//store_demo
import React from 'react';
import { useDispatch, useSelector } from 'react-redux'
import {getNumAction} from '../store/actionCreator'
const Store_demo = () =>{
//使用hook获取状态
const { num } = useSelector(state =>state.store_demo)
//改变状态
const dispatch = useDispatch();
const change_num = () => {
dispatch(getNumAction(num));
}
return (
<div>
<div>--{num}--</div>
<button onClick={change_num}>点击</button>
</div>
)};
export default Store_demo
三、TS+Hooks
再多提供一种TS封装的方式,和上面基本相似,这里就不多介绍了,直接提供代码。
- 公共文件
//store下的index.ts
import thunk from "redux-thunk";
import { combineReducers, ReducersMapObject, AnyAction, createStore, applyMiddleware } from "redux";
import { ICombineState } from "store/index.d";
import Demo from "store/Reducer/demo";
const reducers: ReducersMapObject<ICombineState, AnyAction> = {
demo: Demo,
//[key]:Value, 这里是将每个页面的store引入,相当于注册
};
const store = applyMiddleware(thunk)(createStore)(combineReducers(reducers));
export default store;
//index.d.ts
export interface IAppState {
num: number;
}
export interface ICombineState {
demo: IAppState ;
}
- 页面单独文件
//index.ts
import { AnyAction } from "redux";
import { IAppState } from "store/index.d";
import { NUMBER } from "./actionTypes";
const initialSatte: IAppState = {
num: 0,
};
const reducer = (state: IAppState = initialSatte, action: AnyAction) => {
switch (action.type) {
case NUMBER:
return { ...state, num: action.data };
default:
return state;
}
};
export default reducer;
//actionTypes.ts
// 字体大小
export const NEMBER= Symbol("NEMBER");
//action.ts
import {NEMBER } from "./actionTypes";
export const getNum = (data: number) => {
return {
type: ROOT_FONT_SIZE,
data
};
};
- 使用
import { useSelector, useDispatch } from "react-redux";
import { ICombineState } from "store/index.d";
import { changeRootFontSize } from "store/Reducer/App/action";
const { num} = useSelector((state: ICombineState) => state.demo);
const dispatch = useDispatch();
dispatch(changeRootFontSize(20));