redux 路由是react全家桶【react+redux + router】中的一员,在项目中用于组件状态的管理,使组件传值通信更加方便,统一管理。Redux中文文档地址 http://cn.redux.js.org/
下面我们看使用redux时相关的包。
主要是redux和react-redux这两个包
redux是redux状态管理的核心包提供了创建store、统一管理reducer、绑定action、中间件集成及管理等组件。
react-redux是 使react组件能够使用redux提供的状态管理store,包含两个组件包Provider和connect。
在react项目中一般两者搭配使用。
npm install redux --save --dev-save
npm install react-redux --save --dev-save
npm install redux-thunk --save --dev-save
npm install redux-saga --save --dev-save
import { createStore,combineReducers, applyMiddleware, compose, bindActionCreators,Action }
from 'redux';
import { Provider, connect} from 'react-redux';
import thunkMiddleware from 'redux-thunk'; // action的异步方案选型中间件
import createSagaMiddleware from 'redux-saga'; // action的异步方案选型中间件
redux: 是一个与react相同级别的独立的状态管理模块。 主要功能是创建store状态管理容器并且为store绑定改变store中内容的reducer和为store的派发请求绑定异步请求的中间件[thunk或saga]对组件请求进行拦截处理。
import { createStore, combineReducers, applyMiddleware } from "redux";
----------------------------thunk中间件-----------------------------------------
import thunkMiddleware from "redux-thunk";
export interface rootState {
readonly HistoryReducer: HistoryState;
}
//combineReducers 主要针对多reducer处理
const rootReducer = combineReducers({ HistoryReducer });
const defaultMiddlewares = [thunkMiddleware];
const store = createStore(rootReducer, applyMiddleware(...defaultMiddlewares));
-----------------------------saga中间件----------------------------------------------
import createSagaMiddleware from 'redux-saga'
import rootReducer from './reducers'
import rootSaga from './sagas'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware))
sagaMiddleware.run(rootSaga)
react-redux: 是react和redux的粘合剂,使组件能够使用redux的store进行状态管理。
Provider 使用redux创建的store 包裹整个组件树,使所有被包裹的组件都能够通过props使用store容器中的内容,方便了组件之间的传值。
import { Provider } from "react-redux";
import AppComponent from './AppComponent'
function App() {
return (
// Provider将整个组件进行包裹
);
}
const rootElement = document.getElementById("root");
ReactDOM.render( , rootElement);
connect 将普通UI组件使用 react-redux提供的connect封装为容器组件,这样普通的UI组件就和redux创建的store状态管理容器就绑定连接起来了,组件可以从store中获取属性值,可以发送action触发reducer更新store中的内容。
这个函数组件的文档https://github.com/reduxjs/react-redux/blob/master/docs/api/connect.md#connect
connect(mapStateToProps,mapDispatchToProps,mergeProps,options)函数参数及类型
mapStateToProps?: Function
mapDispatchToProps?: Function | Object
mergeProps?: Function
options?: Object
export class componentApp extends React.Component{
componentWillMount(){}
componentDidMount() {
this.props.fetchFirstQuarter();
}
render() {
const { data } = this.props;
return (
(
{item.content}
)}
/>
);
}
}
const mapstateToprops = (state: rootState) => {
return ({
data: state.HistoryReducer.firstQuarter
})
};
const mapDispatchToProps = {
fetchFirstQuarter
};
export default connect(mapstateToprops, mapDispatchToProps)(componentApp);
[外链图片转存失败(img-z7Xh9hIK-1568965813909)(C:\Users\sunxingba\AppData\Roaming\Typora\typora-user-images\1568711987314.png)]
当通过路由访问到容器组件之后,容器组件先执行connect中的第一个参数的mapstateToprops,从redux创建的store状态容器中获取一次组件映射的属性值,再进入组件的初始化阶段执行constructor方法,挂载阶段执行 componentWillMount-> render -> componentDidMount。挂载完成后组件就加载完毕。如果不进行页面数据的初始加载组件的执行过程就完毕了。
顺序:
mapstateToprops -> constructor -> componentWillMount -> render -> componentDidMount-> componentWillUnmount[切换到其他组件时执行该组件卸载]
一般我们会在页面进行加载数据,这时就需要调用执行connect中的第二个参数的mapDispatchToProps中的函数,进行派发action【这一步会涉及到异步的中间件的使用】执行reducer更新redux创建的store中的内容。【注意:调用函数的位置可以在componentWillMount渲染完成之前和componentDidMount渲染完成之后这两个生命周期函数中。这两个方法中的执行更新store不会影响组件的挂载过程。调用函数的执行是对组件属性进行更新的过程,属于组件的更新阶段。一般推荐在componentDidMount中进行加载数据。】,当在componentDidMount中调用了更新数据的方法后,组件再次执行connect的第一个参数的mapstateToprops,从redux创建的store状态容器中再获取一次最新的组件映射的属性值。再进入组件的更新阶段执行 componentWillReceiveProps->shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate。组件更新完成。
顺序:
mapstateToprops -> constructor -> componentWillMount -> render -> componentDidMount -> fetchFirstQuarter 该方法是在componentDidMount中调用,是在mapDispatchToProps中绑定到组件上 -> mapstateToprops -> componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate -> componentWillUnmount[切换到其他组件时执行该组件卸载]
中间件redux-thunk
redux-thunk可以使store的dispatch方法不仅仅是action对象【{type:"",payload:""}】,还可以 接受一个函数并将dispatch传递给函数,这样我们就可以把action这部分代码提取到单个文件中调用,方便代码管理。在函数中我们可以进行一些请求api调用的异步操作获取数据,在进行dispatch到reducer
import { createStore, combineReducers, applyMiddleware } from "redux";
----------------------------thunk中间件-----------------------------------------
import thunkMiddleware from "redux-thunk";
export interface rootState {
readonly HistoryReducer: HistoryState;
}
//combineReducers 主要针对多reducer处理
const rootReducer = combineReducers({ HistoryReducer });
const defaultMiddlewares = [thunkMiddleware];
const store = createStore(rootReducer, applyMiddleware(...defaultMiddlewares));
actions.tsx文件
thunk的action添加异步调用写法
export const fetchFirstQuarter = () => async dispatch => {
await axios.get("/IfirstQuarter.json").then(data => dispatch({
type: FETCH_FIRST_HISTORTY,
payload: data.data
}) );
}
// 还可以使用fetch调用api
// 这里一般会使用async与await处理异步请求
mapDispatchToProps为react-redux的connect函数的第二个参数,用于绑定操作action。其参数类型为Function | Object => 函数或对象
import { fetchFirstQuarter } from './actions'
mapDispatchToProps写法1
// // mapDispatchToProps为对象类型
// es6中对象属性名称简写
// const mapDispatchToProps = { fetchFirstQuarter }
// 原样
// const mapDispatchToProps = { fetchFirstQuarter:fetchFirstQuarter }
// 解析后为 对象
// const mapDispatchToProps: {
// fetchFirstQuarter: () => (dispatch: any) => void;
// }
mapDispatchToProps写法2
// // mapDispatchToProps为函数类型
const mapDispatchToProps = (dispatch:any)=>{
return ({
fetchFirstQuarter:()=>{
// 此处由于使用了react-thunk中间件,因此dispatch参数可以为一个函数
dispatch(fetchFirstQuarter());
}
})
}
// 解析后为 函数
// const mapDispatchToProps: (dispatch: any) => {
// fetchFirstQuarter: () => void;
// }
export default connect(mapstateToprops, mapDispatchToProps)(FirstQuarter);
在组件中调用action:
this.props.fetchFirstQuarter();
中间件redux-saga
redux-saga中间件是对派发事件action的监听,与thunk相比多个了一层对store.dispathc的监听,通过监听派发事件进而进行拦截事件,在事件中做一些异步请求处理后再继续请求派发到reducer。使用saga后reducer多了这一层监听,对比thunk的好处在于使ui组件与业务处理action更加分离,ui组件的部分不会包含处理action请求的东西。在thunk使用时 UI组件一般需要引入action函数在connect的mapDispatchToProps参数与组件进行绑定。而使用saga则不需要,UI组件操作触发直接dispatch({type:""}),saga通过监听dispatch,拦截后进行业务处理。
saga的引入
import createSagaMiddleware from 'redux-saga';
import rootReducer from './reducers';
import rootSaga from './sagas';// saga处理派发请求的根节点文件
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);//这里注意sagaMiddleware.run 不能放于创建store引用saga中间件之前,否则会出现一个错误 Error: Before running a Saga, you must mount the Saga middleware on the Store using applyMiddleware
sagas.tsx 文件
单层saga
import { takeEvery,call,put } from 'redux-saga/effects'
import { FETCH_FIRST_HISTORTY } from "./history/constant";
import axios from "axios";
export function* fetchFirstQuarter(dispatch:any) {
try {
const result = yield call(axios,"/IfirstQuarter.json");
yield put({
// 这个type的值对应的是reducer中的action.type,此值不能与触发fetchFirstQuarter生成器对应的
// takeEvery("FETCH_FIRST_HISTORTY_ACTION", fetchFirstQuarter)的第一个key参数相同。如 // 果相同会导致死循环。
type: FETCH_FIRST_HISTORTY,
payload: JSON.parse(JSON.stringify(result.data))
});
}catch(err){
console.log(err);
}
}
export default function* root() {
// 当容器组件派发的dispatch的type与takeEvery的第一个参数相同时,执行第二个参数对应的生成器函数。
yield takeEvery("FETCH_FIRST_HISTORTY_ACTION", fetchFirstQuarter);
}
容器组件:
//const mapDispatchToProps = (dispatch:any)=>{
// return ({
// fetchFirstQuarter:()=>{dispatch({type:"FETCH_FIRST_HISTORTY_ACTION"});}
// })
//}
// 或
const action_fetch = ()=>{
return{type:"FETCH_FIRST_HISTORTY_ACTION"}
}
const mapDispatchToProps = (dispatch:any)=>{
return ({
fetchFirstQuarter:()=>{dispatch(action_fetch());}
})
}
export default connect(mapstateToprops, mapDispatchToProps)(FirstQuarter);
在组件中调用action:
this.props.fetchFirstQuarter();
在这个组件中,当调用action进行操作时,fetchFirstQuarter函数只进行了dispatch 类型的派发,没有处理数据或异步请求,真正进行操作的地方在sagas文件中通过生成器函数的taskEvery对该类型的派发监听。
reducer文件 historyReducers.tsx
const initState = {
firstQuarter: [] as ReadonlyArray,
secondQuarter: [] as ReadonlyArray
};
export type HistoryState = Readonly;
export default (state: HistoryState = initState, action): HistoryState => {
switch (action.type) {
case FETCH_FIRST_HISTORTY:
return { ...state, firstQuarter: action.payload };
case FETCH_SECOND_HISTORTY:
return { ...state, secondQuarter: action.payload };
default:
return state;
}
};
在项目中的如果reducer文件就只有一个,那我们就可以直接在创建store的时候就进行进行绑定。
单个reducer引入
import { createStore, applyMiddleware } from "redux";
import thunkMiddleware from "redux-thunk";
import HistoryReducer from "./component/history/historyReducers";
const defaultMiddlewares = [thunkMiddleware];
const store = createStore(HistoryReducer, applyMiddleware(...defaultMiddlewares));
------------------------------------------------------------------------------------
在connect的mapStateToProps中的使用
import { HistoryState } from "./historyReducers";
const mapstateToprops = (state:HistoryState) => {
// 此处对ReadOnly类型的深拷贝
let firstQuarter:IfirstQuarter[] = JSON.parse(JSON.stringify(state.firstQuarter));
return ({
// 此处state获取的属性为reducer中HistoryState类型的属性
data: firstQuarter
})
};
// connect 函数的第一个参数mapstateToprops的类型为Function,所返回值为对象,对象的key为要在组件中映射的属性,值为从store中获取的值
export default connect(mapstateToprops, mapDispatchToProps)(FirstQuarter);
在项目中的我们一般不止一个reducer,我们就需要使用redux包提供的combineReducers来管理所有的reducer并统一导出绑定到store。
多个reducer引入
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunkMiddleware from "redux-thunk";
------------------------------------------------------------------------------------
import HistoryReducer, { HistoryState } from "./component/history/historyReducers";
// 这里是定义每个reducer的state类型
export interface rootState {
readonly HistoryReducer: HistoryState;
// 分号分隔可以写多个
}
// combineReducers 主要针对多reducer处理
export const rootReducer = combineReducers({
HistoryReducer
// 逗号分隔可以写多个
});
------------------------------------------------------------------------------------
const store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
===================================================================================
在connect的mapStateToProps中的使用
import { rootState } from "./historyReducers";
const mapstateToprops = (state:rootState) => {
// 此处对ReadOnly类型的深拷贝
let firstQuarter:IfirstQuarter[] = JSON.parse(JSON.stringify(state.HistoryReducer.firstQuarter));
return ({
// 此处state获取的属性为combineReducers的泛型类型的HistoryReducer类型的属性
data: firstQuarter
})
};
// connect 函数的第一个参数mapstateToprops的类型为Function,所返回值为对象,对象的key为要在组件中映射的属性,值为从store中获取的值
export default connect(mapstateToprops, mapDispatchToProps)(FirstQuarter);
stQuarter:IfirstQuarter[] = JSON.parse(JSON.stringify(state.HistoryReducer.firstQuarter));
return ({
// 此处state获取的属性为combineReducers的泛型类型的HistoryReducer类型的属性
data: firstQuarter
})
};
// connect 函数的第一个参数mapstateToprops的类型为Function,所返回值为对象,对象的key为要在组件中映射的属性,值为从store中获取的值
export default connect(mapstateToprops, mapDispatchToProps)(FirstQuarter);