redux
redux 是一个 react 的状态管理插件。
前言
1. 安装
# NPM
npm install redux
# Yarn
yarn add redux
2. 创建
在 src文件夹下面创建一个store的文件夹,里面创建一个store.js
一、 单个redux
1. store.js
// 这里面的代码写在 store.js 中
// 1. 引入 redux 对应的函数
import { createStore } from "redux"
// 创建一个初始的状态
const state={
uname:"zrs"
}
// 任何修改的值得行为都会在这个函数中进行
// 第一个参数表示的之前的状态,第二个参数是 action 用来判断是哪个状态需要修改,就是dispatch函数传过来的对象
// 这个函数必须有一个返回值,返回值是状态对象
const reducer=(prevState=state,action)=>{
// 这里负责处理修改状态
// 将之前的状态 浅拷贝一份 ,操作浅拷贝的状态对象
const newState={...prevState}
switch(action.type){
case "change-uname":
newState.uname=action.payload
return newState
default:
return prevState
}
}
// 生成一个store
const store = createStore(reducer)
export {store}
2. 在组件中订阅
import {useState,useEffect} from "react"
import {store} from ".../store/index.js"
function Home(){
const [uname,setUname]=useState("")
useEffect(() => {
// 订阅
let unsubscribe=store.subscribe(()=>{
setUname(store.getState().uname)
})
return () => {
// 删除订阅
// 为什么需要删除订阅,因为组件在销毁和创建的时候,store是外来人,不会随着组件的删除和创建而删除和创建,就会导致重复的订阅,所以需要手动删除订阅
unsubscribe();
};
});
return(
<div>
{uname}
</div>
)
}
3. 在组件中发dispatch修改状态
import {store} from ".../store/index.js"
function Center(){
return(
<div>
<button onClick={()=>{
store.dispatch({
type:"change-uname",
payload:"zzr"
})
}}>修改uname</button>
<div/>
)
}
二、redux 模块化
1. user模块
store 的文件夹下面,新建 userReducer.js
const userState={
uname:"zrs"
}
const userReducer=(prevState=userState,action)=>{
const newState={...prevState}
switch(action.type){
case "change-uname":
newState.uname=action.payload
return newState
default:
return prevState
}
}
export {userReducer}
2. city 模块
store 的文件夹下面,新建 cityReducer.js
const cityState={
cityName:"重庆"
}
const cityReducer=(prevState=cityState,action)=>{
const newState={...prevState}
switch(action.type){
case "change-cityName":
newState.cityName=action.payload
return newState
default:
return prevState
}
}
export {cityReducer}
3. 合并的部分
import { createStore,combineReducers } from "redux"
import { userReducer } from ".../userReducer.js"
import { cityReducer } from ".../cityReducer.js"
// combineReducer 用来合并 模块化的reducer
const rootReducer=combineReducer({
userReducer,
cityReducer
})
const store=createStore(rootReducer)
export {store}
4. 在组建中使用
import {useState,useEffect} from "react"
import {store} from ".../store/index.js"
function Home(){
const [uname,setUname]=useState("")
useEffect(() => {
// 订阅
let unsubscribe=store.subscribe(()=>{
// 模块化之后,就这里有了些改变 需要多添加 一个 模块的名称
setUname(store.getState().userReducer.uname)
})
return () => {
unsubscribe();
};
});
return(
<div>
{uname}
</div>
)
}
5. 在组件中发 dispatch 修改状态
这里的操作与单个一样。
import {store} from ".../store/index.js"
function Center(){
return(
<div>
<button onClick={()=>{
store.dispatch({
type:"change-uname",
payload:"zzr"
})
}}>修改uname</button>
<div/>
)
}
三、redux-dispatch 执行异步
redux 中的异步操作需要在action中执行,那么也就是说,dispatch中不再是一个对象,而是一个函数。这个原生redux不能行,需要使用中间价 redux-thunk 或者 redux-promise
1. 安装对应的插件
npm i redux-thunk
2. 使用
// applyMiddleware() 用于加载中间件
import { applyMiddleware, combineReducers, createStore } from "redux";
import { userReducer } from ".../userReducer.js"
import { cityReducer } from ".../cityReducer.js"
// 引入 thunk 支持异步返回函数的方式
import reduxThunk from "redux-thunk";
// combineReducer 用来合并 模块化的reducer
const rootReducer=combineReducer({
userReducer,
cityReducer
})
// applyMiddleware函数的参数可以有多个,表示添加多个中间件
const store=createStore(rootReducer,applyMiddleware(reduxThunk))
export {store}
3. 在action中执行异步操作
在 store 文件夹下面创建一个 actions 文件夹,在这里创建一个 requestUname.js 文件。
function requestUname(uname,pwd){
return (dispatch)=>{
axios({
method:"post",
data:{
uname,
pwd
}
}).then(res=>{
dispatch({
type:"change-uname",
payload:res.data.uname
})
})
}
}
export {requestUname}
4. 在组件中使用
import {store} from ".../store/index.js"
// 引入之前创建发送异步请求的函数
import {requestUname} from ".../store/requestUname.js"
function Center(){
return(
<div>
<button onClick={()=>{
store.dispatch(requestUname(uname,pwd))
}}>修改uname</button>
<div/>
)
}
四、redux 持久化存储
为什么需要持久化存储?
因为数据是在内存中的,只要刷新页面,数据就会丢失或者重置,那么会影响用户体验或者其他操作,比如token,将数据持久化,可以保证数据不丢失,用户刷新页面,依旧是这些数据,体验很好。
持久化存储需要使用一个插件: redux-persist
1. 安装
npm i redux-persist
2. 在 store.js 中使用
import { applyMiddleware, combineReducers, createStore } from "redux";
import { userReducer } from ".../userReducer.js"
import { cityReducer } from ".../cityReducer.js"
// 持久化存储
import { persistStore, persistReducer } from "redux-persist";
// 默认使用localstorage 来持久化
import storage from "redux-persist/lib/storage";
// 引入 thunk 支持异步返回函数的方式
import reduxThunk from "redux-thunk";
// 持久化的配置
const persistConfig = {
key: "root",
storage,
// 白名单中的项,将会被持久化存储
whitelist:['userReducer','cityReducer']
};
const rootReducer = combineReducers({
userReducer,
cityReducer,
});
// 第一个参数是按照什么样的规则持久化,第二个参数是合并之后 rootReducer
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer, applyMiddleware(reduxThunk));
let persistor = persistStore(store);
export { store, persistor };
3. 在项目入口的文件中需要配置一下
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter as RouterSelf } from "react-router-dom";
import { PersistGate } from "redux-persist/integration/react";
// store 中导出另一个
import { persistor } from "./store/index";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<RouterSelf>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</RouterSelf>
);
4. 补充
这个持久化插件,有一个配置项,就是黑名单和白名单。
注意:黑名单和白名单中的值是子reducer的名字。
// 黑名单
const persistConfig = {
key: 'root',
storage: storage,
blacklist: ['cityReducer'] // 黑名单中的项将不会被持久化
};
// 白名单
const persistConfig = {
key: 'root',
storage: storage,
whitelist: ['userReducer'] // 仅仅只有白名单中的项会被持久化
};