为什么需要状态管理,方便组件间的传参,页面也由静态页面变为动态页面.可以更好的处理异步逻辑.
Redux核心概念
可以理解为在页面中点击按钮,然后通过逻辑判断对应的操作,在Store进行对应的逻辑操作,完成操作后通知页面更新.页面也可以直接读取store中的状态,直接进行显示.是一个单向数据流的显示.
redux中间件
- redux 中间件可以对数据进行追溯(可预测、集中管理、可调式)
- 中间件即为中间的处理函数,接收上一个中间件的处理结果
- 核心库中的中间件是同步的,有了中间件可以支持异步
常见的redux中间件
- redux-thunk: 增加函数类型的action
- redux-promise: 流程控制,reject之后不再执行之后逻辑
- redux-sage: 独立管理副作用(高效、可控)
redux 与 redux-thunk结合使用
# 安装包
npm install react-redux
npm install redux
npm install redux-thunk
分别创建以下三个文件:store.js、reducer.js、actios.js
以下是简易案例
//reducer.js
const initialState = {
count: 0
};
export default function reducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1
};
case 'DECREMENT':
return {
count: state.count - 1
};
default:
return state;
}
}
//action.js
//增加
export const increment = () => ({
type: 'INCREMENT'
});
//异步增加
export const incrementAsync = () => {
return (dispatch) => {
setTimeout(() => {
dispatch(increment());
}, 2000);
};
};
//减少
export const decrement = () => ({
type: 'DECREMENT'
});
//store.js
//增加处理中间件方法applyMiddleware
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';
export default createStore(reducer, applyMiddleware(thunk));
页面中的使用
//App.jsx
import React from 'react';
import store from './store';
//useSelector 获取store的数据
//useDispatch 执行状态管理中的方法
//Provider 传递store中的数据
import { Provider, useDispatch, useSelector } from 'react-redux';
import { increment, decrement, incrementAsync } from './action';
function Counter(props) {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
return (
<>
<h3>{count}</h3>
<button
onClick={() => {
dispatch(incrementAsync());
}}
>
+1
</button>
<button
onClick={() => {
dispatch(decrement());
}}
>
-1
</button>
</>
);
}
function App() {
return (
<Provider store={store}>
<div>adasd</div>
<Counter />
</Provider>
);
}
export default App;
手写中间件案例
import { createStore, applyMiddleware } from 'redux';
function logger(store) {
return function (next) {
return function (action) {
console.log('当前的state', store.getState());
console.log('触发的action', action);
//不执行next将不会执行action中的相关方法
next(action);
console.log('next之后的state', store.getState());
};
};
}
export default createStore(reducer, applyMiddleware(logger));
redux 与 redux-sage结合使用
redux-sage理解
- redux sage是redux的中间件 ,管理副作用(异步操作,浏览器缓存操作)
- redux sage使用了generator语法,不是主流的async/await
- redux sage设计了一些列的概念,有学习的门槛
redux sage应用场景
- 相比于redux thunk, 封装更进一层, 流程可控
- 集中处理所有的异步操作
- 以同步的方式来书写异步的代码
# 安装依赖
npm install redux-saga
分别创建以下三个文件:store.js、reducer.js、sagas.js 以下是简易案例
//reducer.js
const initialState = {
count: 0
};
export default function reducer(state = initialState, action) {
switch (action.type) {
case 'INCREMENT':
return {
count: state.count + 1
};
case 'DECREMENT':
return {
count: state.count - 1
};
default:
return state;
}
}
//sagas.js
import { delay, put, takeEvery } from 'redux-saga/effects';
function* incrementAsync(action) {
try {
yield delay(2000);
yield put({ type: 'INCREMENT' });
} catch (error) {}
}
function* decrementAsync(action) {
try {
yield delay(2000);
yield put({ type: 'DECREMENT' });
} catch (error) {}
}
export default function* rootSaga() {
yield takeEvery('INCREMENT_ASYNC', incrementAsync);
yield takeEvery('DECREMENT_ASYNC', decrementAsync);
}
//store.js
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducer';
import createSagaMiddleware from 'redux-saga';
import rootSaga from './sagas';
const sagaMiddleware = createSagaMiddleware();
function logger(store) {
return function (next) {
return function (action) {
console.log('当前的state', store.getState());
console.log('触发的action', action);
next(action);
console.log('next之后的state', store.getState());
};
};
}
export default createStore(reducer, applyMiddleware(sagaMiddleware, logger));
sagaMiddleware.run(rootSaga);
页面中的使用
import React from 'react';
import store from './store';
import { Provider, useDispatch, useSelector } from 'react-redux';
function Counter(props) {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
const action = (type) => dispatch({ type });
return (
<>
<h3>{count}</h3>
<button
onClick={() => {
action('INCREMENT_ASYNC');
}}
>
+1
</button>
<button
onClick={() => {
action('DECREMENT_ASYNC');
}}
>
-1
</button>
</>
);
}
function App() {
return (
<Provider store={store}>
<div>adasd</div>
<Counter />
</Provider>
);
}
export default App;
redux toolkit
为什么使用redux toolkit
- redux的项目会有很多模板代码, 还可能写错、冲突等
- redux的store过于复杂,需要比较清晰的目录结构
- redux需要配合immer、thunk、reselect...
- redux团队官方插件
- 提供架构思想: 合并reducer&action , 自动异步逻辑
- 代码简洁高效,并自带一些列整合好的功能
#安装
npm install @reduxjs/toolkit
分别创建以下三个文件:store.js、reducer.js、action.js 以下是简易案例
//action.js
import { createAction } from '@reduxjs/toolkit';
export const increment = createAction('INCREMENT');
export const decrement = createAction('DECREMENT');
//reducer.js
import { createReducer } from '@reduxjs/toolkit';
import { increment, decrement } from './action';
const initialState = {
count: 0
};
const todosReducer = createReducer(initialState, (builder) => {
builder
.addCase(increment, (state, action) => {
state.count += action.payload || 1;
})
.addCase(decrement, (state, action) => {
state.count -= 1;
})
.addDefaultCase((state, action) => {
state.count = 0;
});
});
export default todosReducer;
//store.js
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducer';
function logger(store) {
return function (next) {
return function (action) {
console.log('当前的state1', store.getState());
console.log('触发的action2', action);
next(action);
console.log('next之后的state3', store.getState());
};
};
}
export default createStore(reducer, applyMiddleware(logger));
页面中的使用
import React from 'react';
import store from './store';
import { Provider, useDispatch, useSelector } from 'react-redux';
import { increment, decrement } from './action';
function Counter(props) {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
return (
<>
<h3>{count}</h3>
<button
onClick={() => {
dispatch(increment());
}}
>
+1
</button>
<button
onClick={() => {
dispatch(decrement());
}}
>
-1
</button>
</>
);
}
function App() {
return (
<Provider store={store}>
<div>adasd</div>
<Counter />
</Provider>
);
}
export default App;
项目实战案例
#安装需要使用到的插件
#状态管理:react-redux redux
#中间件:redux-thunk(简化使用)
#数据持久化:redux-persist
yarn add react-redux redux redux-persist redux-thunk
在项目中新建store文件夹,用于配置状态管理
文件夹/文件 | 描述 |
---|---|
store/config/action | 设置操作的方法 |
store/config/actionTypes | 定义类型 |
store/config/reducer | 数据 |
store/getters/config | 定义获取存储在store中的数据 |
store/getters/index | 统一导出数据 |
store/index | 状态管理配置入口 |
store/action | 统一导出各个模块的action文件 |
以下将以config目录下的配置文件做为展示
// store/config/actionTypes.ts
//水印控制
const WATER_MASK = "WATER_MASK"
//面包屑控制
const BREAD_CRUMB = 'BREAD_CRUMB'
//标签栏控制
const TAB = 'TAB'
//主题色
const PRIMARY_COLOR = 'PRIMARY_COLOR'
//导航模式
const NAVIGATION_MODE = 'NAVIGATION_MODE'
export {
WATER_MASK,
BREAD_CRUMB,
TAB,
PRIMARY_COLOR,
NAVIGATION_MODE
}
// store/config/action.ts
///<reference path="../../model/store/config.ts"/>
import * as ActionTypes from '@/store/config/actionTypes'
/**
* 设置水印
* @param {boolean} waterMask
*/
export const setWaterMask = (waterMask:boolean) => ({
type:ActionTypes.WATER_MASK,
waterMask
})
/**
* 设置面包屑
* @param {boolean} breadCrumb
*/
export const setBreadCrumb = (breadCrumb:boolean) => ({
type: ActionTypes.BREAD_CRUMB,
breadCrumb
})
/**
* 设置标签栏
* @param {boolean} tab
*/
export const setTab = (tab:boolean) => ({
type:ActionTypes.TAB,
tab
})
/**
* 设置主题色
* @param {string} primaryColor
*/
export const setPrimaryColor = (primaryColor:string) => ({
type:ActionTypes.PRIMARY_COLOR,
primaryColor
})
/**
* 设置导航模式
* @param {ModelStoreConfig.Navigation} navigationMode
*/
export const setNavigationMode = (navigationMode:ModelStoreConfig.Navigation) => ({
type:ActionTypes.NAVIGATION_MODE,
navigationMode
})
// store/config/reducer.ts
///<reference path="../../model/store/config.ts"/>
import * as actionTypes from '@/store/config/actionTypes'
import * as globalConfig from "@/config/global_config"
interface ModelStoreConfig {
waterMask:boolean, //水印
breadCrumb:boolean, // 面包屑
tab:boolean, //标签栏
diyLayout:boolean //自定义布局
openColor:boolean // 自定义颜色
primaryColor:string // 主题色
navigationMode:ModelStoreConfig.Navigation // 导航模式
}
const config:ModelStoreConfig = {
waterMask:globalConfig.waterMask,
breadCrumb:globalConfig.breadCrumb,
tab:globalConfig.tab,
diyLayout:globalConfig.diyLayout,
openColor:globalConfig.openColor,
primaryColor:globalConfig.primaryColor,
navigationMode:globalConfig.navigationMode
}
export default function reducer(state = config,action:any){
const {type,waterMask,breadCrumb,tab,primaryColor,navigationMode} = action
switch (type) {
case actionTypes.WATER_MASK:
return {...state,waterMask}
case actionTypes.BREAD_CRUMB:
return {...state,breadCrumb}
case actionTypes.TAB:
return {...state,tab}
case actionTypes.PRIMARY_COLOR:
return {...state,primaryColor}
case actionTypes.NAVIGATION_MODE:
return {...state,navigationMode}
}
return state
}
配置getters实现类似于双向数据获取
// store/getters/config.ts
/**
* 获取水印
* @param state
*/
export const getWaterMask = (state:any) => state.config.waterMask
/**
* 获取面包屑
* @param state
*/
export const getBreadCrumb = (state:any) => state.config.breadCrumb
/**
* 获取标签栏
* @param state
*/
export const getTab = (state:any) => state.config.tab
/**
* 获取主题色
* @param state
*/
export const getPrimaryColor = (state:any) => state.config.primaryColor
/**
* 获取导航模式
* @param state
*/
export const getNavigationMode = (state:any) => state.config.navigationMode
// store/getters/index.ts
export * from "./config"
配置入口
// store/index.ts
import {
createStore,
combineReducers,
applyMiddleware,
compose
} from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import UserReducer from './user/reducer'
import RouteReducer from './route/reducer'
import NotPersistenceReducer from './notPersistence/reducer'
import ConfigReducer from './config/reducer'
import thunk from 'redux-thunk'
//整合各个模块的数据
const reducer = combineReducers({
user: UserReducer,
route: RouteReducer,
notPersistence: NotPersistenceReducer,
config: ConfigReducer
})
//配置数据持久化
const persistConfig = {
key: 'root', // localstorage存储的key名为 root
storage, // 以localstorage方式存储
whitelist: ['user', 'route', 'config'] // whitelist 意思是只对这几个文件做持久化的处理
}
//整合后的数据
const myReducer = persistReducer(persistConfig, reducer)
const store = createStore(
myReducer,
//使用compose 整合多个方法,类似于数组. 使用applyMiddleware 利用中间件,优化调用redux的使用
compose(
applyMiddleware(thunk),
//开启redux的调试工具栏
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)
)
const persistor = persistStore(store)
export { store, persistor }
页面的使用
import { useSelector, useDispatch } from 'react-redux'
import { setWaterMask } from "@/store/action"
import { getWaterMask } from "@/store/getters"
const ThemeContainer = () => {
//useSelector 获取redux数据
//useDispatch 执行redux的方法
const dispath = useDispatch()
return (
<>
<div>是否显示水印:{useSelector(getWaterMask)}</div>
<button onClick={() => dispatch(setWaterMask(!useSelector(getWaterMask)))}>修改水印显示</button>
</>
)
}
export default ThemeContainer
感谢阅读!