Redux是怎么个事?一文整明白点!
Redux是什么
Redux是一个全局状态管理的工具。
ps:现在官方推荐使用Redux Toolkit,本篇会优先讲解Redux的方法,会在有些地方使用优化后的@reduxjs/toolkit方法。
Redux有什么
他是状态管理的工具,那么就是要存储、操作状态。我们现在用一个简单的需求来尝试了解使用redux。 需求:维护一个数字状态为计数器,可以增加或者删除这个数字。
定义
存储store
状态(数据)存储在一个叫store
的对象里。这个对象跟一般的直接存储数据不一样,可以先把它看成是 以reducer
作为一个存储的单位。reducer包含了数据,以及操作数据的方法。
import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
先介绍reducer
,明白了之后我们再来看怎么操作,获取数据。
reducer
reducer 是一个函数,它接收当前state
和一个action
对象,决定如何在必要时更新状态,并返回新状态:(state, action) => newState
。可以将 reducer 视为一个事件侦听器,它根据接收到的操作(事件)类型处理事件。
这是一个基础的reducer
const counterReducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
};
counterReducer
为一个reducer
第一个参数state
为我们存储的目标值也就是计数器的数字。这里state可以默认赋值(这里是0)。action
是告诉reducer应该执行什么操作。
reducer里面必须返回一个值,这个值是我们的目标数据(这里是计数器数字),在经过我们想要的操作之后存储到我们的store
里。可以先认为这里的return就是直接更新了我们的store数据。
所以reducer里面就是装着我们要操作数据的逻辑,比如我们要增加、减少数字,这里就可以定义我们的逻辑,也就是
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
定义好了,我们应该怎么使用呢。 想要触发我们定义的逻辑,使用dispatch
来触发
dispatch
Redux 存储有一个名为 dispatch
的方法。更新状态的唯一方法是调用 store.dispatch()
并传入一个操作对象。
store.dispatch({ type: 'INCREMENT' })
这里的操作就是告诉reducer去执行INCREMENT的逻辑。计数器数字默认值为0,操作完之后就为1.
可以使用react-redux
中的useDispatch
,更简洁,不需要跟store进行关联。那怎么获取数据呢。
import { useDispatch } from 'react-redux';
export const useCounterActions = () => {
const dispatch = useDispatch();
return {
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
}
}
getState() / selector
store提供一个getState()
方法来获取到数据。
store.getState();
// {counter: -1}
这里获取的是store里面所有的数据。我们前面注册了counter,这里就会获得counter的值。可以使用Selector来更符合官网的写法,避免重复逻辑,代码更易读。
import { useSelector } from 'react-redux';
export const useCounter = () => useSelector(state => {
return state.counter
});
代码小结
完整的代码,部分使用了react-redux
,可以作为store.js的文件的代码。
import { configureStore } from '@reduxjs/toolkit';
import { useDispatch, useSelector } from 'react-redux';
const counterReducer = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
console.log(`counterReducer:`, state);
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
};
export const store = configureStore({
reducer: {
counter: counterReducer,
app: appReducer,
},
});
export const useCounter = () => useSelector(state => {
return state.counter
});
export const useCounterActions = () => {
const dispatch = useDispatch();
return {
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
}
}
使用
我们定义完了之后,使用的时候还是需要搭配些其他的东西才能正常使用。
Provider
使用react-redux中的Provider
包裹需要的组件,或者直接包裹根组件,这样下面的组件都可以直接使用。
使用方法:
- 引入Provider
- Provider组件选择要注入的store 代码示例
import { Provider } from "react-redux";
import { store } from "./store";
const inter = Inter({ subsets: ["latin"] });
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<Provider store={store}>
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
</Provider>
);
}
直接使用
被provider包裹的子组件都可以直接调用上面我们store注册的方法,比如dispatch、selector
import { useCounter, useCounterActions } from "./store";
export default function Home() {
const value = useCounter();
const { increment, decrement } = useCounterActions();
return (
<div style={{height: '100vh'}}>
<h1>Counter: {value}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
优化
createSlice
reducer可以使用Redux Toolkit中的createSlice
,它旨在简化 Redux 应用的状态管理逻辑,特别是减少在定义 action creators 和 reducers 时需要编写的样板代码。Redux Toolkit 是官方推荐的 Redux 实践方式,其设计目标是使得 Redux 更易于使用,并优化性能。
作用和原理
createSlice
函数接收一个包含 name
、initialState
和 reducers
三个主要字段的对象作为参数,并自动生成对应的 action creators 和 action types。它还自动处理 reducer 逻辑的内部细节,使得我们可以专注于状态的修改逻辑而非繁琐的样板代码。
参数详解
- name: 这是一个字符串,代表 slice 的名称,用于生成 action types。例如,name 为 "user" 会生成形如 "user/add" 的 action type。
- initialState: 这是 slice 的初始状态值。它可以是任何有效的 JavaScript 类型(如对象、数组、数字等)。
- reducers: 一个对象,键是描述状态更改的方法的名称,值是对应的 reducer 函数。每个 reducer 函数会接收两个参数:当前的 state 和一个 action 对象,其中 action 对象的
payload
字段通常用于传递新的数据。
改造下我们上面的计数器
import { configureStore, createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: state => state + 1,
decrement: state => state - 1,
}
});
使用的方式也改下
export const useCounterActions = () => {
const dispatch = useDispatch();
return {
increment: () => dispatch(counterSlice.actions.increment()),
decrement: () => dispatch(counterSlice.actions.decrement()),
}
}
实现一个小的Redux
用createstore
来写。对标上面的configureStore
。
createStore 函数
这个函数接收两个参数:reducer
和 preloadedState
。reducer
是一个函数,用于根据当前的 state 和传递给它的 action 来计算并返回新的 state。preloadedState
是初始时的 state,可用于在应用启动时设置一个初始状态。
1. 初始化 state
函数开始时,我们将 preloadedState
赋值给变量 state
。这样做可以确保当 store 被创建时,它已经有了一个初始的状态。
2. 存储监听函数
创建一个名为 listeners
的数组来存储所有需要在 state 更新时被通知的监听函数。
3. getState 方法
深拷贝一份state,返回这个 state
。提供了一个从外部访问当前 state 的方式,而无需直接访问 state
变量,防止引用类型对象被修改。
4. subscribe 方法
这个方法接收一个 listener
函数作为参数,并将这个函数加入到 listeners
数组中。此方法返回一个 unsubscribe
函数,该函数可以从 listeners
数组中移除已注册的监听器。这提供了一种动态管理 state 变化监听的机制。
5. dispatch 方法
dispatch
方法是 Redux 架构中最核心的部分。它执行以下步骤:
- 接受一个
action
对象作为参数。 - 调用 reducer 函数,传入当前的
state
和action
,并获得一个新的 state。 - 更新
state
变量为这个新的 state。 - 遍历
listeners
数组,调用每个监听函数,这样它们就可以响应 state 的更新。
通过这个过程,dispatch
方法连接了 action 的发送和 state 的响应更新,是 Redux 状态管理的核心。
6. 初始化调用
最后,在 createStore
的末尾,dispatch
方法被用一个特殊的 action { type: '@@redux/INIT'}
调用。这是一个初始化动作,用于设置 reducer 中的默认状态。这个初始化动作确保所有 reducer 能够返回它们的初始状态(如果有的话)。
7. 返回的对象
createStore
函数返回一个包含 dispatch
、subscribe
和 getState
方法的对象。这个对象就是所谓的 "store",它提供了操作和访问状态的接口。
function createStore(reducer, preloadedState) {
let state = preloadedState
const listeners = []
function getState() {
let _state= { ...state } //做些深拷贝处理
return _state
}
function subscribe(listener) {
listeners.push(listener)
return function unsubscribe() {
const index = listeners.indexOf(listener)
listeners.splice(index, 1)
}
}
function dispatch(action) {
state = reducer(state, action)
listeners.forEach(listener => listener())
}
dispatch({ type: '@@redux/INIT' })
return { dispatch, subscribe, getState }
}
至此,Redux的流程应该都全部熟悉了。现在开始React里面的全局状态管理吧!
转载自:https://juejin.cn/post/7374238968685723674