🤔 Redux + React-Redux 工作原理
曾几何时,Redux 作为 React 全家桶的固定成员,几乎是面试必考题。Redux 做了什么?解决了什么问题?原理是啥?Hooks出来之后,我们还需要它吗?
1. 适用场景
当一份数据需要全局共享时,如果不使用 Redux 状态管理工具,不按照一定规律处理状态的读写,你的代码很快就会变成一团乱麻。
这个时候你需要一种机制,可以在全局同一个地方查询状态、改变状态、传播状态的变化。
Redux 就是这个机制。
2. Redux是怎么做的?
首先,所有的状态,都保存在一个对象里。
store
:保存数据的地方state
: 是store当前时刻的快照,可以通过store.getState()
获得action
: 表示当前要执行的操作,是一个对象const action = { type: 'ADD_TODO', payload: 1 }
dispatch
: 是发出Action的唯一办法reduer
: store收到action后,必须给出一个全新的state,这样view才发生变化。这种state的计算过程就叫reducer。
Reducer 函数最重要的特征是,它是一个纯函数。只要是同样的输入,必定得到同样的输出。1
2
3
4
5
6// 必须是全新的对象
function reducer(state, action) {
return Object.assign({}, state, { thingToChange });
// 或者
return { ...state, ...newState };
}
整个流程如下:
让用户发出action,Reducer函数算出新的State, View重新渲染。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const defaultState = 0;
const reducer = (state = defaultState, action) => {
switch (action.type) {
case 'ADD':
return state + action.payload;
default:
return state;
}
};
// store.dispatch触发reduer的自动执行
const state = reducer(1, {
type: 'ADD',
payload: 2
});
3. 实现一个Redux
我们来实现一个Redux1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// store
const createStore = (reducer) => {
let state;
let listeners = [];
const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(listener => listener());
};
const subscribe = (listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter(l => l !== listener);
}
};
dispatch({});
return { getState, dispatch, subscribe };
};
4. 视图更新
数据和操作数据的逻辑有了,剩下的问题就是,计算出新的state后,怎么触发视图更新?
这时需要用到React-Redux
。React-Redux
需要解决两个问题:
- state对象如何转化为组件的参数?
- 用户发出的Action如何从UI组件传出去?
React-Redux
提供 connect
方法,用connect包裹你的组件来创建一个高阶组件,它会传递dispatch方法和Redux储存的state,并作为props传递到组件中。
connect
让组件变成能响应state变化的组件。connect
方法接收两个参数,mapStateToProps 和 mapDispatchToPropsmapStateToProps
会将state映射到组件的props上。当state更新的时候,会触发 UI 组件的重新渲染。mapDispatchToProps
会redux里的dispatch方法传递到组件的props上
1 | import React from 'react'; |
5. useReducer 代替 Redux
但是,自从hooks出来之后,新版 React 结合reducer和Context,就可以直接替代Redux。
state 存在于顶层组件中,由 useReducer 管理,Context 进行分发。子组件可以轻松获取 state 和 dispatch。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import { createContext, useReducer } from 'react';
export const TasksContext = createContext(null);
export const TasksDispatchContext = createContext(null);
export function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider value={dispatch}>
{children}
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
}
// 组件中获取task和dispatch
const dispatch = useContext(TasksDispatchContext);
const tasks = useContext(TasksContext);
也可以再封装一层1
2
3
4
5
6
7
8export function useTasks() {
return useContext(TasksContext);
}
export function useTasksDispatch() {
return useContext(TasksDispatchContext);
}
const tasks = useTasks();
const dispatch = useTasksDispatch();
6. 现在的Redux
那还用redux吗?最新版Redux在提供什么解决方案?最新包叫Redux Toolkit
我觉得核心优势只剩下createSlice
, 让你使用 Immer 库 来编写 reducer,不需要使用拓展运算符,消除意外的 mutations1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import { createSlice } from "@reduxjs/toolkit";
const todosSlice = createSlice({
name: "todos",
initialState: [],
reducers: {
todoAdded(state, action) {
state.push({
id: action.payload.id,
text: action.payload.text,
completed: false,
});
},
todoToggled(state, action) {
const todo = state.find((todo) => todo.id === action.payload);
todo.completed = !todo.completed;
},
},
});