当多个组件的state互相依赖,且组件间无法进行有效通信时,我们希望把这些state抽离出来进行集中管理,各组件可按照一定的规则进行状态的读写,这就是redux提供的功能
一款状态管理工具,用于集中管理应用中(多个组件共享)的state
有下面三个核心概念:
store 整个应用中的state都存储在一棵object tree上,并且这个 object tree 只存在于唯一一个 store 中。store不可直接修改,需要结合下面的action和reducer 来完成
import { createStore } from 'redux'
import reducer from './reducers'
// 创建
const store = createStore(reducer)
action 要想更新 state 中的数据,你需要发起一个 action,action描述发生了什么
{ type: 'ADD_TODO', value: 'Go to swimming pool' }
reducer 为了把 action 和 state 串起来,开发一些函数,这就是 reducer。action通过dispatch派发,到达 reducer,通过自定义处理事件,返回新的 state 的函数,完成state的修改
function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([{ text: action.value, completed: false }]);
default:
return state;
}
}
react的官方绑定库
redux工具箱,规范了redux逻辑的编写方式
动机:
1、解决redux存储配置复杂问题
2、解决redux引入依赖过多问题
3、解决redux样例代码过多问题
redux持久化插件
通过把数据存储在localstorage中,使页面刷新后还能保持原来的状态
npm install --save redux react-redux @reduxjs/toolkit redux-persist
通过RTK提供的configureStore创建store,而不是使用原生redux提供的createStore,configureStore 方法还提供了中间件的配置,默认提供了三个中间件,也可添加额外的 中间件
import { combineReducers } from "redux";
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterReducer";
import storage from 'redux-persist/lib/storage';
import autoMergeLevel2 from 'redux-persist/es/stateReconciler/autoMergeLevel2';
import { persistReducer } from "redux-persist";
// 持久化存储设置
const persistConfig = {
key: "root",
storage: storage,
stateReconciler: autoMergeLevel2,
// whitelist: ["counter"],
// blacklist: ["counter"]
};
// 合成reducer
const reducer = combineReducers({
counter: counterReducer,
})
const myPersistReducer = persistReducer(persistConfig, reducer);
// 配置store
export default configureStore({
reducer: myPersistReducer,
// devTools: process.env.NODE_ENV !== 'production',
// middleware: [ thunk ]
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false, // toolkit默认添加的 可序列化检查中间件
// immutabilityCheck: false, // 不变性检查
// thunk: false,
}).concat(),
})
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import reportWebVitals from './reportWebVitals';
import Page from './pages';
import { Provider } from 'react-redux'
import store from './redux/store';
import { PersistGate } from 'redux-persist/integration/react';
import { persistStore } from 'redux-persist';
const root = ReactDOM.createRoot(document.getElementById('root'));
let persistor= persistStore(store)
root.render(
<React.StrictMode>
{/* 通过provide,使所有组件都可以访问到store */}
<Provider store={store}>
<PersistGate loading={<div>loading...</div>} persistor={persistor}>
<Page />
</PersistGate>
</Provider>
</React.StrictMode>
);
reportWebVitals();
createSlice 一个函数,它接受初始state、reducer函数的对象和名称,并自动生成与reducer和state相对应的动作创建者和动作类型。
这个 API 是编写 Redux 逻辑的标准方法
import { createSlice} from "@reduxjs/toolkit";
export const counterSlice = createSlice({
name: "counter",
initialState: {
value: 10
},
reducers: {
// toolkit引用了immer的库,允许在createSlice中直接修改state
increment: (state) => {
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
}
}
})
export const { increment, decrement, incrementByAmount} = counterSlice.actions
export default counterSlice.reducer
通过react-redux提供的useSelector、useDispatch可以直接获取到store中的数据和通过dispatch触发reducer,修改state
import React,{useState} from 'react'
import { useSelector, useDispatch, shallowEqual } from 'react-redux'
import { decrement, increment, incrementByAmount, } from '../../redux/counterReducer'
import styles from "./style.module.scss"
const Counter = () => {
const [amount,setAmount] = useState(1)
const dispatch = useDispatch()
// shallowEqual-在组件决定是否被渲染之前,会进行一次浅比较如果该组件依赖的state并没有被更改,不进行渲染
// 获取store
const {value} = useSelector((state) => state.counter, shallowEqual)
return (
<div className={styles.counterContainer}>
<div>
<p>{value}</p>
<p>状态:{status}</p>
<button onClick={() => dispatch(increment())} >
加一
</button>
<button onClick={() => dispatch(decrement())} >
减一
</button>
<br /><br />
<input value={amount} onChange={(e) => setAmount(Number(e.target.value))}/>
<button onClick={() => dispatch(incrementByAmount(amount))}>+</button>
</div>
</div>
)
}
export default Counter