React Redux 学习手记
React Redux is the official React UI bindings layer for Redux. It lets your React components read data from a Redux store, and dispatch actions to the store to update state.
(Excerpted from react-redux.js.org)
Redux 是 JavaScript 应用的状态容器,提供可预测的状态管理。
想做全局变量管理,于是来学
基本概念
单向数据流
有以下基本流程:
- 用 state 来描述应用程序在特定时间点的状况
- 基于 state 来渲染出 view
- 当发生某些事情时(例如用户单击按钮),state 会根据发生的事情进行更新,生成新的 state
- 基于新的 state 重新渲染 view
不可变性
原来的对象或数组中的内容不改变,通过复制的方式先获取一份 copy,然后更新 copy 中的内容。
const obj = {
a: {
// 为了安全的更新 obj.a.c,需要先复制一份
c: 3
},
b: 2
}
const obj2 = {
// obj 的备份
...obj,
// 覆盖 a
a: {
// obj.a 的备份
...obj.a,
// 覆盖 c
c: 42
}
}
const arr = ['a', 'b']
// 创建 arr 的备份,并把 c 拼接到最后。
const arr2 = arr.concat('c')
// 或者,可以对原来的数组创建复制体
const arr3 = arr.slice()
// 修改复制体
arr3.push('c')
Redux 期望所有状态更新都是使用不可变的方式。
state, view, action
- state: 储存数据的一个个“状态”
- view: 当前绘制出的 UI
- action: 由用户交互而触发的事件,可以引起 state 的更新,进而重新渲染 view.
- action 是一个具有
type
字段的普通 JavaScript 对象。 type
字段是一个字符串,给这个 action 一个描述性的名字,比如"todos/todoAdded"
。我们通常把那个类型的字符串写成“域/事件名称”,其中第一部分是这个 action 所属的特征或类别,第二部分是发生的具体事情。- action 对象可以有其他字段,其中包含有关发生的事情的附加信息。按照惯例,我们将该信息放在名为
payload
的字段中。
- action 是一个具有
// Example for action
const addTodoAction = {
type: 'todos/todoAdded',
payload: 'Buy milk'
}
reducer
reducer 是一个函数,接收当前的 state
和一个 action
对象,必要时决定如何更新状态,并返回新状态。函数签名是:(state, action) => newState
。
你可以将 reducer 视为一个事件监听器,它根据接收到的 action(事件)类型处理事件。
Reducer 必需符合以下规则:
- 仅使用
state
和action
参数计算新的状态值 - 禁止直接修改
state
。必须通过复制现有的state
并对复制的值进行更改的方式来做 不可变更新(immutable updates)。 - 禁止任何异步逻辑、依赖随机值或导致其他“副作用”的代码
const initialState = { value: 0 }
function counterReducer(state = initialState, action) {
// 检查 reducer 是否关心这个 action
if (action.type === 'counter/increment') {
// 如果是,复制 `state`
return {
...state,
// 使用新值更新 state 副本
value: state.value + 1
}
}
// 返回原来的 state 不变
return state
}
store
当前 Redux 应用的状态存在于一个名为 store 的对象中。
store 是通过传入一个 reducer 来创建的,并且有一个名为 getState
的方法,它返回当前状态值.
import { configureStore } from '@reduxjs/toolkit'
const store = configureStore({ reducer: counterReducer })
console.log(store.getState())
// {value: 0}
dispatch
dispatch 一个 action 可以形象的理解为 “触发一个事件”。
Reducer 就像事件监听器一样,当它们收到关注的 action 后,它就会更新 state 作为响应。
store.dispatch({ type: 'counter/increment' })
console.log(store.getState())
// {value: 1}
const increment = () => {
return {
type: 'counter/increment'
}
}
store.dispatch(increment())
console.log(store.getState())
// {value: 2}
辅助函数类型
action creator
action creator ([text => action])是一个创建并返回一个 action 对象的函数。它的作用是让你不必每次都手动编写 action 对象.
const addTodo = text => {
return {
type: 'todos/todoAdded',
payload: text
}
}
selector
Selector 函数可以从 store 状态树中提取指定的片段.
const selectCounterValue = state => state.value
const currentValue = selectCounterValue(store.getState())
console.log(currentValue)
// 2
functions provided by toolkit
createSlice
可以定义 初始状态, reducer 函数, slice name, 然后自动生成相应的action creator 和 action type.
内部重写了实现逻辑,可以使用可变的方式来进行状态修改。
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
interface CounterState {
value: number
}
const initialState = { value: 0 } as CounterState
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment(state) {
state.value++
},
decrement(state) {
state.value--
},
incrementByAmount(state, action: PayloadAction<number>) {
state.value += action.payload
},
},
})
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
Return values:
{
name : string,
reducer : ReducerFunction,
actions : Record<string, ActionCreator>,
caseReducers: Record<string, CaseReducer>
}
Redux 数据流
具体来说,对于 Redux,我们可以将这些步骤分解为更详细的内容:
- 初始启动:
- 使用最顶层的 root reducer 函数创建 Redux store
- store 调用一次 root reducer,并将返回值保存为它的初始
state
- 当 UI 首次渲染时,UI 组件访问 Redux store 的当前 state,并使用该数据来决定要呈现的内容。同时监听 store 的更新,以便他们可以知道 state 是否已更改。
- 更新环节:
- 应用程序中发生了某些事情,例如用户单击按钮
- dispatch 一个 action 到 Redux store,例如
dispatch({type: 'counter/increment'})
- store 用之前的
state
和当前的action
再次运行 reducer 函数,并将返回值保存为新的state
- store 通知所有订阅过的 UI,通知它们 store 发生更新
- 每个订阅过 store 数据的 UI 组件都会检查它们需要的 state 部分是否被更新。
- 发现数据被更新的每个组件都强制使用新数据重新渲染,紧接着更新网页
Practice
好,现在我已经掌握了 React Redux 的基本用法了
Store.tsx
import {configureStore, createSlice} from '@reduxjs/toolkit';
const DataSlice = createSlice({
name: 'data',
initialState: {loggedIn: false, role: '', childProps: {}, parentProps: {}},
reducers: {
setLoggedIn: (state, action) => {
state.loggedIn = action.payload;
},
setRole: (state, action) => {
state.role = action.payload;
},
setChildProps: (state, action) => {
state.childProps = action.payload;
},
setParentProps: (state, action) => {
state.parentProps = action.payload;
},
},
});
export const {setLoggedIn, setRole, setChildProps, setParentProps} =
DataSlice.actions;
export default configureStore({
reducer: {data: DataSlice.reducer},
});
App.tsx
import {Provider} from 'react-redux';
import Child from './UI/Child';
const App = () => {
return (
<Provider store={Store}>
<View />
</Provider>
);
};
然后就可以在代码中调用 Store 提供的 setLoggedIn, setRole, setChildProps, setParentProps 这些 action creators 和 dispatch 方法了.