Redux简单记录
Redux使用有感
Redux简述
Redux是一个全局状态管理工具,与pinia不同,它并不依托与某个框架绑定使用。当然很多基于React搭建的项目都使用Redux作为全局状态管理工具。本文涉及的示例也是基于React的。
Redux的特点:
- 不可变更新,即更新数据的方式永远是重新赋一个全新的值,而不是直接修改旧值。
- 通过所谓的
action函数去dispatch我们配置的全局store,store里的reducer函数去更新数据(action和reducer存在绑定关系 ---Toolkit)。 - 纯函数,即更新数据的函数必须是纯函数,即函数的返回值只依赖于函数的输入值,而不依赖于函数的外部状态。
- 单向数据流,即数据的流向是单向的,只能从
action流向reducer。
本文主要讲述Toolkit的实现模板,这也是Redux官方目前所推崇的配置方式
Tips⭐⭐:
Redux Toolkit在reducer函数中使用immer库去让你可以通过直接修改的方式更新数据,而不是重新返回一个新值。但需要注意的是immer只是通过proxy去帮我们去做返回新值的操作,本质上reducer函数永远接受一个新值,而不是直接修改旧值。
Redux的构成
通过示例去理解Redux的构成,帮助我们去理解Redux的工作原理以及所提到的名词。
首先从根文件出发
import { configureStore } from '@reduxjs/toolkit';
import postsReducer from './features/postSlice';
import usersReducer from './features/usersSlice';
import notificationsReducer from './features/notificationsSlice';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
const store = configureStore({
reducer: {
posts: postsReducer,
users: usersReducer,
notifications: notificationsReducer,
},
});
export default store;
export type RootState = ReturnType<typeof store.getState>;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export const useAppDispatch: () => typeof store.dispatch = useDispatch;
-
configureStore帮助我们去创建了一个store对象,这是全局且唯一的根对象。 -
configureStore接受一个对象参数配置去创建store对象,其中reducer这个 属性可以传入我们创建的Reducer片段。或者换个理解方式,假设项目的全局状态分为三大板块,分别是posts、user、notice,我们最好对他们分开进行处理。这样我们就分别给它们创建一个Reducer片段,各自的Reducer片段去处理各自的状态。(Reducer后续会提到的,稍安勿躁)。 -
默认导出
store,这个store对象就是我们全局唯一的根对象。后续我们所有的全局数据操作都会在这个store对象上进行。
后面的还有三行代码是自定义我们的Selector、Dispatch触发器并提供类型推导,帮助我们在项目中处理全局状态时提供类型提示和约束---(JS可无视)
Reducer片段
const notificationsSlice = createSlice({
name: 'notifications',
initialState: [] ,
reducers: {
allNotificationsRead(state) {
state.forEach((notification) => {
notification.read = true;
});
},
},
});
export default notificationsSlice.reducer;
export const { allNotificationsRead } = notificationsSlice.actions;
export const selectAllNotifications = (state: RootState) => state.notifications;
-
createSlice创建了一个Reducer片段,它接受一个对象参数,其中name属性是我们的Reducer片段的名称,initialState属性是我们的Reducer片段的初始状态。 -
reducers属性则是我们的Reducer片段的reducer函数列表,它接受一个对象参数,其中的键值就是我们的reducer函数,键名就是我们的reducer函数的名称。(这里我使用ES6的简写形式)createSlice会自动生成与与reducer同名的action构造函数,所以下面我们可以具名导出与之同名的action构造函数 -
默认导出我们的
Reducer片段,这个Reducer片段将传入我们的根store并通过state.notifications访问。 -
具名导出我们
Recducer片段的action构造函数(allNotificationsRead),这些action构造函数接受参数返回一个action对象从而通过dispatch去触发数据操作。 -
同时我们导出了一个
selector函数(selectAllNotifications),通过useSelector触发并返回我们Reducer片段的当前state(当前片段state位于store.notifications)。
使用示例(片段)
import { useAppSelector, useAppDispatch } from '@/store';
import { selectAllUsers } from '@/store/features/usersSlice';
import { selectAllNotifications, allNotificationsRead } from '@/store/features/notificationsSlice';
import { useLayoutEffect } from 'react';
export default function NotificationsList() {
const notifications = useAppSelector(selectAllNotifications);
const users = useAppSelector(selectAllUsers);
const dispatch = useAppDispatch();
useLayoutEffect(()=>{
dispatch(allNotificationsRead())
})
}
useAppSelector是我自定义的useSelector仅提供类型推导支持--可看作useSelector
useAppDispatch是我自定义的useDispatch仅提供类型推导支持--可看作useDispatch
-
useAppSelector触发我们的selector函数,并返回当前state的数据。通过我们具名导出的selector(selectAllNotifications,selectAllUsers)函数去获取我们的notifications和user的当前state。 -
useAppDispatch返回store.dispatch,并传入具名导出的action构造函数(allNotificationsRead)所返回的action对象去触发Reducer
...关于涉及异步操作的部分,后续再补充。
