Redux
由js实现的flux思想第三方状态管理库,可以与react、vue、angular甚至jQuery结合使用
一、redux介绍及设计与使用三大原则
- state以单一对象存储在store对象中
- state只读(每次都返回一个新的对象)
- 使用纯函数reducer执行state更新
纯函数定义
1、对外界没有副作用
2、同样的输入无论执行多少次都得到同样的输出
二、redux 工作流

三、redux用法
创建store
//store/redux.js
import { createStore } from "redux";
// 处理函数
const reducer = (prevState = { show: true }, action) => {
let newState = { ...prevState };
switch (action.type) {
case "show":
newState.show = true;
return newState;
break;
case "hide":
newState.show = false;
return newState;
break;
default:
return prevState;
break;
}
};
const store = createStore(reducer);
export default store;
store发布
import store from '../store/redux'
useEffect(() => {
// dispatch action对象
store.dispatch({
type: "show",
});
return () => {
store.dispatch({
type: "hide",
});
};
}, []);
store订阅和取消订阅
import store from "../store/redux";
const [showState, setShowState] = setState();
//执行完后返回取消订阅的函数
let unsubsribe = store.subsribe(() => {
//转化为自己的状态
const { show } = store.getState();
setShowState(show);
});
//取消订阅
unsubsribe()
四、redux基本原理
基本原理是发布订阅模式,通过reducer更新状态
function createStore(reducer) {
let list = [];
let state = reducer(undefined, {});
function dispatch(action) {
// 新状态 老状态
state = reducer(state, action);
for (let i in list) {
list[i] && list[i]();
}
}
function subsribe(callback) {
list.push(callback);
}
function getState() {
return state;
}
return {
dispatch,
subsribe,
getState,
};
}
五、reducer合并
如果不同的action所处理的属性之间没有联系,可以把Reducer函数拆分,最终合成一个大的Reducer
combineReducers
import ARdecuder from "./Rdecuders/ARdecuder";
import BRdecuder from "./Rdecuders/BRdecuder";
import { combineReducers } from "redux";
const reducer = combineReducers({
ARdecuder,
BRdecuder,
});
访问
要加上对应的reducer(不同的命名空间)
store.subsribe(() => {
//store.getState(): {ARdecuder:{},BRdecuder:{}}
const { show } = store.getState().ARdecuder;
setShowState(show);
});
六、中间件(middleware)
用于在action和reducer中间架起一道桥梁来异步处理。
中间件的由来和机制
function thunkMiddleware({ dispatch, getState }) {
return (next) => (aciton) => {
typeof aciton === "function" ? aciton(dispatch, getState) : next(aciton);
};
}
中间件接收到参数action,当action不是函数时就和之前一样执行next方法(下一步处理);是函数就先调用action函数,在action处理完成后就在内部调用dispatch。
常用异步中间件
redux-thunk
action参数需要是一个function。
安装redux-thunk
npm i redux-thunk
注册redux-thunk
import { createStore, applyMiddleware } from "redux";
import reduxThunk from "redux-thunk";
const store = createStore(reducer, applyMiddleware(reduxThunk));
dispatch action函数
import store from "../store/redux";
useEffect(() => {
store.dispatch(getListAction());
}, []);
异步action
// getListAction.js
function getListAction() {
axios.get("").then((res) => {
store.dispatch({
type: "data",
data: res.data,
});
});
}
对所有reducer分发,最后匹配到对应reducer
// getListrReducer
function getListrReducer(prevState, aciton) {}
redux-promise
redux-promise唯一不同的是action需要是一个promise对象。redux-promise会自动dispatch。
安装redux-promise
npm i redux-promise
注册redux-promise
可以同时注册多个中间件,现在action可以是一个函数也可以是一个promise
import { createStore, applyMiddleware } from "redux";
import reduxThunk from "redux-thunk";
import reduxPromise from "redux-promise";
const store = createStore(reducer, applyMiddleware(reduxThunk,reduxPromise));
异步action需要返回一个promise
// 异步action promise
function getListAction() {
return axios.get("").then((res) => {
return {
type: "data",
data: res.data,
};
});
}
async function getListAction() {
let list = await axios.get("").then((res) => {
return {
type: "data",
data: res.data,
};
});
return list;
}
七、redux开发者工具引入
redux-devtools
redux-devtools需要增加配置
import { createStore, applyMiddleware, compose } from 'redux';
import reduxThunk from "redux-thunk";
import reduxPromise from "redux-promise";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer,composeEnhancers(
applyMiddleware(reduxThunk,reduxPromise)
));
react-redux
react-redux通过Provider组件和connect,把redux的state和dispatch都映射到props,从而使得不需要用redux的API自己订阅getState和分发dispatch。原理是把组件变为UI组件,通过connect生成一个容器组件作为该组件的父组件,通过porps把数据传给该组件。
一、UI组件和容器组件
UI组件
- 只负责UI的呈现,不带有任何业务逻辑
- 没有状态
- 所有数据都有参数props提供
- 不使用任何redux的API
容器组件
- 负责管理数据和业务逻辑,不负责UI的呈现
- 带有内部状态
- 使用redux的API
二、Provider和connect
react-redux提供Provider组件,可以让容器组件拿到state
//index.js
import store from './store';
import { Provider } from "react-redux";
import App from "./App.js";
import ReactDom from "react-dom";
ReactDom.render(
<Provider store={store}>
<App />
</Provider>
);
组件里使用connect
//Detail.js
import getListAction from "./getListAction";
import { connect } from "react-redux";
function Detail(props) {
// 需要用到多个porps的值作为依赖项要解构出来
// 不用向redux订阅存到状态里,直接来props的
let { list, change, getListAction, hide, isShow } = props;
useEffect(() => {
if (list.length == 0) {
getListAction();
}
return () => {
hide();
};
}, [list, getListAction]);
return (
<div>
{isShow && <div>show</div>}
<ul>
{list.map((item) => {
<li
onClick={(item) => {
change(item);
}}
>
{item.name}
</li>;
})}
</ul>
</div>
);
}
// 映射state为props
const mapStatetoProps = (state) => {
return {
isShow: state.isShow,
list: state.list,
};
};
// 映射dispatch为props
// 用对象包裹,可以是任何多个任何形式函数,也可以传参。调用时需要把参数传过滤
const mapDispatchtoProps = {
getListAction,
hide: () => {
return {
type: "hide",
};
},
change(item) {
return {
type: "change",
value: item,
};
},
};
//connect两个参数,不传写null,第一个是将来给孩子的属性,第二个是将来给孩子的函数
export default connect(mapStatetoProps, mapDispatchtoProps)(Detail);
三、HOC和context通信在react-redux底层中的应用
- connect是HOC,高阶组件
- Provider组件可以让容器拿到state,使用了context
HOC不仅仅是一个方法,确切说是一个组件工厂,获取低阶组件,生成高阶组件。它可以
- 代码复用,代码模块化
- 增删改props
- 渲染劫持(修改样式等)
四、connect原理
function connect(callback, obj) {
let value = callback();
// 返回一个函数,参数是组件,把低阶组件转成高阶组件
return (MyComponent) => {
// 返回一个函数式组件,因为组件外部包了一层父组件,路由的相关属性被放到了这一层父组件
return (props) => {
return (
<div>
<MyComponent {...value} {...obj} {...props} />
</div>
);
};
};
}
五、redux持久化
redux-persist
浏览器刷新,redux存起来的数据就会消失,有必要对数据进行持久化
安装
npm i redux-persist -S
使用
//store.js
import ARdecuder from "./Rdecuders/ARdecuder";
import BRdecuder from "./Rdecuders/BRdecuder";
import { combineReducers, createStore, applyMiddleware } from "redux";
import { persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import reduxThunk from "redux-thunk";
// storage存起来的key
const persistConfig = {
key: "root",
storage,
whitelist: ["ARdecuder"], // 只有白名单的reducer会持久化
//blacklist: ['navigation'] // 只有黑名单的reducer不会持久化
};
const reducer = combineReducers({
ARdecuder,
BRdecuder,
});
// 对reducer持久化
const persistedReducer = persistReducer(persistConfig, reducer);
const store = createStore(persistedReducer, applyMiddleware(reduxThunk));
// 生成持久化的store
let persistor = persistStore(store);
export { store, persistor };
在App.js使用也可以
//index.js
import { PersistGate } from "redux-persist/integration/react";
import { Provider } from "react-redux";
import App from "./App.js";
import ReactDom from "react-dom";
ReactDom.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
);