redux 相关的基础知识及用法,包括中间件redux-thunk的使用
基础知识点巩固
-
redux三大原则
- 单一数据源:整个应用的state被存储在一个对象数中,并且这个对象数只存在于唯一的store中。
- State是只读的:唯一改变state的方法是出发action,action是一个描述已发生事件的普通对象。
- 使用纯函数来执行修改:编写reducer来描述action如何改变state。
-
使用redux-thunk来dispatch异步action
-
middleware的作用是在action于reducer之间执行副作用函数,如日志打印(redux-logger),异步函数(redux-thunk),他的优点在于能够链式的插入多个中间件。
目录结构:
(根目录)
src
|-assets // 公共资源
|-components // 公共组件
|-pages // 页面
|-router // 路由
|-store // 公共状态管理 redux
|-action-types.js // 行为标识(必须是唯一的)
|-action.js // 常说的,发起一个action。通过函数传入新的数据,替换之前的store。
|-reducer.js // 相当于一个管理员,基于不同的行为标识,修改store中不同的状态。
|-store.js // store相当于一个仓库,存储各种数据。这个文件的作用是,整合文件。
App.js //
index.js // 入口文件
1. index.js 文件
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter as Router } from "react-router-dom";
// 还没有用到 redux 的时候,引用 Provider 等,会报错。
// Provider 标签需要放在最外层
import {Provider} from 'react-redux';
import store from "./store/store";
if (module.hot) {
module.hot.accept();
}
ReactDOM.render(
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>
,
document.getElementById('root')
);
2. action-types.js 文件
export const HTMLFONTSIZE = 'HTMLFONTSIZE';
export const DATAONE = 'DATAONE';
export const DATATWO = 'DATATWO';
// ...
3. action.js 文件
/*
没有使用 react-thunk , 只能返回一个对象
*/
// import * as Types from './action-types';
// let actions = {
// changeFontSize(obj) {
// return { type: Types.HTMLFONTSIZE, obj }
// }
// };
// export default actions;
/*
引入thunk插件后,我们可以在actionCreators内部编写逻辑,处理请求结果。而不只是单纯的返回一个action对象。
*/
import * as Types from './action-types';
function changeFontSize(param) {
return (dispatch) => {
setTimeout(() => { // 模拟异步(这里可以是一次数据请求)
dispatch(changeFontSizeAction(param));
}, 3000)
}
}
function changeFontSizeAction(obj) {
return { type: Types.HTMLFONTSIZE, obj }
}
export {
changeFontSize
};
4. reducer.js 文件
import * as Types from './action-types';
const obj = {
htmlFontSize: 0
}
function reducer(state = obj, action) {
switch (action.type) {
case Types.HTMLFONTSIZE:
return {...state, ...action.obj};
default:
return JSON.parse(JSON.stringify(state));
}
}
export default reducer;
5. store.js 文件
/*
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
// window._store = store; //在window上挂一个叫_store的属性,可以看到store里面的内容
export default store;
*/
// 使用 thunk 后,可以这样写:
import { createStore, applyMiddleware } from "redux";
import reducer from './reducer';
import thunk from "redux-thunk";
const store = createStore(
reducer,
applyMiddleware(thunk)
)
export default store;
使用 thunk 中间件。以及:combineReducers、applyMiddleWare、compose
使用包含自定义功能的 middleware 来扩展 Redux 是一种推荐的方式。 例如,redux-thunk 支持 dispatch function,以此让 action creator 控制反转。 被 dispatch 的 function 会接收 dispatch 作为参数,并且可以异步调用它。 这类的 function 就称为 thunk。另一个 middleware 的示例是 redux-promise。 它支持 dispatch 一个异步的 Promise action,并且在 Promise resolve 后可以 dispatch 一个普通的 action。 compose 可以将中间件组合拼装,使组件更加强大。
使用中间件的 store
import { createStore, combineReducers, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
const store = createStore(
//如果有多个 reducer ,将多个 reducer 整合在一起(项目只有一个reducer时,不建议使用并且会出现错误提示)
combineReducers(reducer, reducer1, reducer2),
applyMiddleware(thunk) //可以在 action 中做异步处理 dispatch
//如果有多个中间件就可以这样写:compose(applyMiddleware(thunk), xxxx, xxxxx)
)
这样,就可以使用 redux 了。
在此之前,我们习惯:哪里需要用到接口,就在哪个页面(文件)中调取。
现在,通过中间件 thunk 可以让程序员在 action 中调用接口。
下面具体说一下,中间件 thunk 的使用方式。
使用 applyMiddleware(thunk) 之后,action 中可以这样写:
import * as Types from './action-types';
function getList(param) {
return (dispatch) => {
setTimeout(() => { // 模拟异步(这里可以是一次数据请求)
dispatch listData(param)
}, 5000)
}
}
function listData(obj) {
return { type: Types.LISTDATA, obj }
}
export {
getList
};
项目组件中使用的例子:
import { getList } from "../../store/store";
import React, { Component } from "react";
import "./BaseEchart.less";
import connect from "react-redux/es/connect/connect";
class BaseEchart extends Component {
constructor(props) {
super(props);
};
componentDidMount() {
const param = {
startTime: "2019-10-20 12:00:00",
endTime: "2019-10-21 12:00:00"
}
this.props.getList(param)
}
// UNSAFE_componentWillMount,
componentWillReceiveProps(nextProps) {
if (nextProps.listData !== this.props.listData) {
console.log(nextProps.actions.listData)
}
}
//componentWillUnmount() {}
render() {
return (
<div className="BaseEchart">
</div>
)
}
}
export default connect(state => state, {getList})(BaseEchart);