目录
前言
Redux 中文官网
React Redux 英文官网 和 React Redux 中文文档
Redux Toolkit 英文官网 和 Redux 中文官网之 Redux Toolkit 介绍
Redux DevTools 中文官网
Redux 是一个全局应用状态管理库。
Redux 的基本思想:应用中使用集中式的全局状态来管理,并明确更新状态的模式,以便让代码具有可预测性。
Redux 期望所有状态更新都是使用不可变的方式——更新原数据的复本。
并非所有应用程序都需要 Redux,那么,何时需要考虑使用 Redux 呢?
- 应用中有很多 state 在多个组件中需要使用。
- 应用 state 会随着时间的推移而频繁更新。
- 更新 state 的逻辑很复杂。
- 中型和大型代码量的应用,很多人协同开发。
结合 React 一般使用 react-redux(见下文)。
安装 react-redux:
npm i -S react-redux
一、Redux 包
- React Redux:Redux 可以集成到任何的 UI 框架中,其中最常见的是 React 。React-Redux 是我们的官方包,它可以让 React 组件访问 state 片段和 dispatch actions 更新 store,从而同 Redux 集成起来。
- Redux Toolkit:Redux Toolkit 是我们推荐的编写 Redux 逻辑的方法。 它包含我们认为对于构建 Redux 应用程序必不可少的包和函数。 Redux Toolkit 构建是我们建议的最佳实践中,简化了大多数 Redux 任务,预防了常见错误,并使编写 Redux 应用程序变得更加容易。
- Redux DevTools:Redux DevTools 扩展 可以显示 Redux 存储中状态随时间变化的历史记录。这允许你有效地调试应用程序,包括使用强大的技术,如“时间旅行调试”。
二、Redux 的核心成员
1、Action
Action 是一个对象。用来描述应用程序中发生了什么的事件。
Action 需要通过 store.dispatch() 方法来发送。
Action 对象包含 2 个属性:
- type 属性:给该 action 起一个名字。语法格式为——“
域/事件名称
”——“该 action 的特征或类别 / 该 action 表示的具体事情”。 - payload 属性:附加信息。
例如:
const addTodoAction = {
type: 'todos/todoAdded',
payload: 'Buy milk'
}
2、Reducer
Reducer 是一个纯函数。它是一个事件监听器,它根据接收到的 action(事件)类型处理事件。
Reducer 函数接收 2 个参数:
- state:原来的 state。
- action:一个 action 对象,必要时决定如何更新状态,并返回新状态。
【拓展】Reducer 是一个纯函数,所以绝对不要在 reducer 里面做一些引入 副作用 的事情。比如:
- 直接修改 state 参数对象。
- 请求 API。
- 调用不纯的函数,比如: Data.now() Math.random()。
(1)、Reducer 的规则
- 仅使用 state 和 action 参数计算新的状态值
- 禁止直接修改 state。必须通过复制现有的 state 并对复制的值进行更改的方式来做 不可变更新(immutable updates)。
- 禁止任何异步逻辑、依赖随机值或导致其他“副作用”的代码。
(2)、Reducer 函数内部的实现原理
- 检查 reducer 是否关心这个 action。
- 如果是,则复制 state,使用新值更新 state 副本,然后返回新 state。
- 否则,返回原来的 state 不变。
例如:
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
}
(3)、根 Reducer 的优化
根 reducer(root reducer)是管理整个 State 的 reducer。
Redux 里,一个 Store 对应一个 State 状态,所以整个 State 对象就应该由一个总的 reducer 函数管理。但是,如果所有的状态更改逻辑都放在这一个 reducer 里面,显然会变得巨大而难以维护。得益于纯函数的实现,我们只需要让状态树上的每个字段都有一个自己的 reducer 函数来管理,就可以将大的 reducer 拆分成多个小的 reducer 了。然后在根 reducer 里合并成一个大的 reducer。
在根 reducer 里合并 reducerA 和 reducerB:
function someApp(state = {}, action) {
return {
a: reducerA(state.a, action),
b: reducerB(state.b, action)
};
}
为了简化 reducer 合并,Redux 提供了一个工具函数 combineReducers()。
import { combineReducers } from 'redux';
const someApp = combineReducers({
a: reducerA,
b: reducerB
});
3、Store
Store 是一个对象。
应用中应有且仅有一个 Store。
Store 的作用:
- 用来存放整个应用的 State 状态树。
- 提供一个 getState() 方法获取 State。
- 提供一个 dispatch() 方法发送 action 更改 State。
- 提供一个 subscribe() 方法注册回调函数监听 State 的更改。
(1)、创建一个 Store 对象
官方推荐使用 @reduxjs/toolkit 包(见下文)的 configureStore() 方法来创建一个 store。需要给 configureStore 方法传入一个 reducer 对象。通过 getState() 的方法可以获取当前 store 中 state 对象。
例如:
import { configureStore } from '@reduxjs/toolkit'
const store = configureStore({ reducer: counterReducer })
console.log(store.getState()) // {value: 0}
当然,也可以直接使用 Redux 包的 createStore() 方法来创建一个 store。
例如:
import { createStore } from 'redux';
import someApp from './reducers';
let store = createStore(someApp);
// 你也可以额外指定一个初始 State(initialState),这对于服务端渲染很有用
// let store = createStore(someApp, window.STATE_FROM_SERVER);
(2)、使用 Store 通过 Dispatch 分发 Action
Dispatch 是一个函数。它是更新 state 的唯一方法。store 通过调用 Dispatch 来执行所有 reducer 函数,并最终计算出更新后的 state。
Dispatch 函数接收 1 个参数:一个 action 对象。 返回一个新的 state 对象,若该 state 对象没有发生变化则与原来一样。
例如:
// 使用 subscribe 方法创建一个函数,监听 State 的更改
let unsubscribe = store.subscribe(() => console.log(store.getState()));
// 分发 action
store.dispatch({ type: 'CHANGE_A' });
store.dispatch({ type: 'CHANGE_B', payload: 'Modified b' });
// 停止侦听状态更新
unsubscribe();
// 获取更新后的 state 对象
console.log(store.getState())
三、React 中使用 Redux 简例
import {createStore} from 'redux';
function count(state = 0, action) {
switch (action.type) {
case 'ADD':
return state + 1
case 'REDUCER':
return state - 1;
default:
return state
}
}
// 创建一个仓库
let store = createStore(count);
let currentValue = store.getState();
console.log('当前的值:', currentValue);
// 定义一个监听的方法
let listener = () => {
const previosValue = currentValue;
currentValue = store.getState();
console.log('上一个值:', previosValue, '当前值:', currentValue)
}
// 创建一个监听
store.subscribe(listener);
// 分发任务
store.dispatch({type:"ADD"});
store.dispatch({type:"ADD"});
store.dispatch({type:"ADD"});
store.dispatch({type:"REDUCER"});
四、Redux 的 API
1、Redux 的 API
(1)、createStore
创建一个包含程序完整 state 树的 Redux store。
createStore 可以接收 3 个参数:
- reducer (Function): 接收两个参数,分别是当前的 state 树和要处理的 action,返回新的 state 树。
- [preloadedState] (any): 初始时的 state。你可以决定是否把服务端传来的 state 水合(hydrate)后传给它,或者从之前保存的用户会话中恢复一个传给它。如果你使用 combineReducers 创建 reducer,它必须是一个普通对象,与传入的 keys 保持同样的结构。否则,你可以自由传入任何 reducer 可理解的内容。
- enhancer (Function): Store enhancer。你可以选择指定它以使用第三方功能,如middleware、时间旅行、持久化来增强 store。Redux 中唯一内置的 store enhander 是 applyMiddleware()。
createStore 返回值一个 Store。保存了应用程序所有 state 的对象。改变 state 的惟一方法是 dispatch action。你也可以 subscribe state 的变化,然后更新 UI。
(2)、Store
Store() 方法:
- getState():返回应用当前的 state 树。 它与 store 的最后一个 reducer 返回值相同。
- dispatch(action):dispatch action。这是触发 state 变化的惟一途径。
- subscribe(listener):添加一个变化监听器。每当 dispatch action 的时候就会执行,state 树中的一部分可能已经变化。你可以在回调函数里调用 getState() 来拿到当前 state。
- replaceReducer(nextReducer):替换 store 当前用来计算 state 的 reducer。这是一个高级 API。只有在你需要实现代码分隔,而且需要立即加载一些 reducer 的时候才可能会用到它。在实现 Redux 热加载机制的时候也可能会用到。
(3)、combineReducers
随着应用变得越来越复杂,可以考虑将 reducer 函数 拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分。
combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。
合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 由 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名。
通过为传入对象的 reducer 命名不同的 key 来控制返回 state key 的命名。
通常的做法是命名 reducer,然后 state 再去分割那些信息,这样你可以使用 ES6 的简写方法:combineReducers({ counter, todos })。这与 combineReducers({ counter: counter, todos: todos }) 是等价的。
combineReducers 方法接收一个参数:
- reducers: 一个对象,它的值(value)对应不同的 reducer 函数,这些 reducer 函数后面会被合并成一个。下面会介绍传入 reducer 函数需要满足的规则。
combineReducers 方法返回一个调用 reducers 对象里所有 reducer 的 reducer,并且构造一个与 reducers 对象结构相同的 state 对象。
(4)、applyMiddleware
使用包含自定义功能的 middleware 来扩展 Redux 是一种推荐的方式。Middleware 可以让你包装 store 的 dispatch 方法来达到你想要的目的。同时, middleware 还拥有“可组合”这一关键特性。多个 middleware 可以被组合到一起使用,形成 middleware 链。其中,每个 middleware 都不需要关心链中它前后的 middleware 的任何信息。
Middleware 最常见的使用场景是无需引用大量代码或依赖类似 Rx 的第三方库实现异步 actions。这种方式可以让你像 dispatch 一般的 actions 那样 dispatch 异步 actions。
applyMiddleware 方法的参数:
- …middleware (arguments):遵循 Redux middleware API 的函数。每个 middleware 接受 Store 的 dispatch 和 getState 函数作为命名参数,并返回一个函数。该函数会被传入被称为 next 的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用 next(action),或者在其他需要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store 的 dispatch 方法作为 next 参数,并借此结束调用链。所以,middleware 的函数签名是 ({ getState, dispatch }) => next => action。
applyMiddleware 方法的返回值:一个应用了 middleware 后的 store enhancer。这个 store enhancer 的签名是 createStore => createStore,但是最简单的使用方法就是直接作为最后一个 enhancer 参数传递给 createStore() 函数。
(5)、bindActionCreators
把一个 value 为不同 action creator 的对象,转成拥有同名 key 的对象。同时使用 dispatch 对每个 action creator 进行包装,以便可以直接调用它们。
一般情况下你可以直接在 Store 实例上调用 dispatch。如果你在 React 中使用 Redux,react-redux 会提供 dispatch 函数让你直接调用它 。
惟一会使用到 bindActionCreators 的场景是当你需要把 action creator 往下传到一个组件上,却不想让这个组件觉察到 Redux 的存在,而且不希望把 dispatch 或 Redux store 传给它。
为方便起见,你也可以传入 action creator 作为第一个参数,并且得到一个 dispatch 函数作为返回值。
bindActionCreators 方法的参数:
- actionCreators (Function or Object): 一个 action creator,或者一个 value 是 action creator 的对象。
- dispatch (Function): 一个由 Store 实例提供的 dispatch 函数。
bindActionCreators 方法的返回值:一个与原对象类似的对象,只不过这个对象的 value 都是会直接 dispatch 原 action creator 返回的结果的函数。如果传入一个单独的函数作为 actionCreators,那么返回的结果也是一个单独的函数。
(6)、compose
从右到左来组合多个函数。
这是函数式编程中的方法,为了方便,被放到了 Redux 里。
当需要把多个 store enhancers 依次执行的时候,需要用到它。
compose 方法的参数:
- (arguments): 需要合成的多个函数。预计每个函数都接收一个参数。它的返回值将作为一个参数提供给它左边的函数,以此类推。例外是最右边的参数可以接受多个参数,因为它将为由此产生的函数提供签名。(译者注:compose(funcA, funcB, funcC) 形象为 compose(funcA(funcB(funcC()))))
compose 方法的返回值:从右到左把接收到的函数合成后的最终函数。
2、React Redux 的 API
(1)、Provider 组件
Provider 组件使需要访问 Redux 存储的任何嵌套组件都可以使用 Redux 存储器。
由于 React Redux 应用程序中的任何 React 组件都可以连接到商店,因此大多数应用程序将在顶层呈现 Provider 组件,整个应用程序的组件树都在其中。
(2)、connect() 方法
connect 仍然有效,并且在 React Redux 8.x 中受支持。在 React 推行 Hooks 之后,Redux 官方推荐使用 React-Redux hooks API (useSelector 和 useDispatch)作为默认方法来代替 Connect() 方法。
connect() 函数将React组件连接到 Redux 存储。
它为其连接的组件提供了它需要的来自存储的数据片段,以及它可以用来将操作分派到存储的函数。
它不会修改传递给它的组件类;相反,它返回一个新的、连接的组件类,该类包装了您传入的组件。
mapStateToProps和mapDispatchToProps分别处理Redux存储的状态和调度。state和dispatch将作为第一个参数提供给mapStateToProps或mapDispatchToProps函数。
function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)
mapStateToProps和mapDispatchToProps的返回在内部分别称为stateProps和dispatchProps。如果已定义,它们将作为第一个和第二个参数提供给mergeProps,其中第三个参数将是ownProps。组合结果通常称为mergedProps,然后将提供给连接的组件。
connect 方法接受 4 个可选的参数:
- mapStateToProps:Function。mapStateToProps?: (state, ownProps?) => Object。
- mapDispatchToProps:Function | Object。mapDispatchToProps?: Object | (dispatch, ownProps?) => Object。
- mergeProps:Function。mergeProps?: (stateProps, dispatchProps, ownProps) => Object。
- options:Object。options?: Object。
connect 方法返回值:是一个包装函数,它接受您的组件,并返回一个包装组件及其注入的附加属性。
(3)、batch() 方法
如果您使用的是 React 18,则不需要使用该 API。React 18 自动批处理所有状态更新,无论它们在何处排队。
React 的 unstable_batchedUpdates() API 允许将事件循环标记中的任何 React 更新批处理到单个呈现过程中。React 已经在内部将其用于自己的事件处理程序回调。这个 API 实际上是 ReactDOM 和 React Native 等渲染器包的一部分,而不是 React 核心本身。
由于 React Redux 需要同时在 ReactDOM 和 React Native 环境中工作,因此我们已经在构建时从正确的渲染器导入了这个 API,以供自己使用。我们现在也公开重新导出这个函数,重命名为 batch() 方法。可以使用它来确保在 React 之外调度的多个操作只会导致单个渲染更新。
(4)、Hooks(★★★★★)
①、 useSelector()
允许您使用选择器函数从Redux存储状态提取数据。
选择器函数应该是纯函数,因为它可能在任意时间点多次执行。
例如:
import React from 'react'
import { useSelector } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector((state) => state.counter)
return <div>{counter}</div>
}
默认情况下,useSelector() 将在调度操作后运行选择器函数时对所选值进行引用相等比较,并且仅在所选值发生更改时才会导致组件重新呈现。但是,与connect() 不同,useSelector() 不会阻止组件因其父级重新渲染而重新渲染,即使组件的属性没有更改。如果需要进一步的性能优化,可以考虑将函数组件包装在React.memo() 中:
const CounterComponent = ({ name }) => {
const counter = useSelector((state) => state.counter)
return (
<div>
{name}: {counter}
</div>
)
}
export const MemoizedCounterComponent = React.memo(CounterComponent)
②、useDispatch()
这个钩子从Redux存储区返回对调度函数的引用。您可以根据需要使用它来分派操作。
例如:
import React from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
return (
<div>
<span>{value}</span>
<button onClick={() => dispatch({ type: 'increment-counter' })}>
Increment counter
</button>
</div>
)
}
当使用dispatch将回调传递给子组件时,有时可能需要使用useCallback将其记忆。如果子组件尝试使用React优化渲染行为。memo() 或类似的方法,这样可以避免由于回调引用的更改而不必要地呈现子组件。
import React, { useCallback } from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
const incrementCounter = useCallback(
() => dispatch({ type: 'increment-counter' }),
[dispatch]
)
return (
<div>
<span>{value}</span>
<MyIncrementButton onIncrement={incrementCounter} />
</div>
)
}
export const MyIncrementButton = React.memo(({ onIncrement }) => (
<button onClick={onIncrement}>Increment counter</button>
))
只要将同一存储实例传递给 Provider 组件,调度函数引用就会稳定。通常,该存储实例在应用程序中从不更改。
然而,React 钩子 lint 规则不知道分派应该是稳定的,并且会警告分派变量应该添加到 useEffect 和 useCallback 的依赖数组中。最简单的解决方案就是这样做:
export const Todos = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(fetchTodos())
// 安全地将调度添加到依赖项数组
}, [dispatch])
}
③、useStore()
这个钩子返回对传递给 Provider 组件的同一个 Redux 存储的引用。
这个钩子可能不应该经常使用。首选使用 Selector() 作为主要选择。然而,这对于需要访问商店的不太常见的场景可能有用,例如更换减速器。
例如:
import React from 'react'
import { useStore } from 'react-redux'
export const CounterComponent = ({ value }) => {
const store = useStore()
// 如果存储状态更改,组件将不会自动更新(仅供参考!不要在真正的应用程序中这样做)。
return <div>{store.getState()}</div>
}
3、Redux Toolkit 的 API
Redux Toolkit 目前有 7 个 API:
- configureStore():封装了createStore,简化配置项,提供一些现成的默认配置项。它可以自动组合 slice 的 reducer,可以添加任何 Redux 中间件,默认情况下包含 redux-thunk,并开启了 Redux DevTools 扩展。
- createReducer():帮你将 action type 映射到 reducer 函数,而不是编写 switch…case 语句。另外,它会自动使用 immer 库来让你使用普通的 mutable 代码编写更简单的 immutable 更新,例如 state.todos[3].completed = true。
- createAction():生成给定 action type 字符串的 action creator 函数。该函数本身已定义了 toString(),因此可以代替常量类型使用。
- createSlice():接收一组 reducer 函数的对象,一个 slice 切片名和初始状态 initial state,并自动生成具有相应 action creator 和 action type 的 slice reducer。
- createAsyncThunk:接收一个 action type 字符串和一个返回值为 promise 的函数, 并生成一个 thunk 函数,这个 thunk 函数可以基于之前那个 promise ,dispatch 一组 type 为 pending/fulfilled/rejected 的 action。
- createEntityAdapter:生成一系列可复用的 reducer 和 selector,从而管理 store 中的规范化数据。
- createSelector:来源于 Reselect 库,重新 export 出来以方便使用。
五、Redux 运行机制与工作原理
Redux 使用 “单向数据流”:数据驱动视图的更新,用户在视图上操作,触发actions,通过action的方法更改state里的数据。形成一个数据单向流动的闭环。
- State 是驱动应用的真实数据源头。它描述了应用程序在某个时间点的状态,视图基于该 state 渲染。
- 当应用程序中发生某些事情时,state 会根据发生的事情进行更新:
- 视图 dispatch 一个 action
- store 调用 reducer,随后根据发生的事情来更新 state
- store 将 state 发生了变化的情况通知 UI
- 视图基于新 state 重新渲染
具体来说,对于 Redux,我们可以将这些步骤分解为更详细的内容:
- 初始启动:
- 使用最顶层的 root reducer 函数创建 Redux store。
- store 调用一次 root reducer,并将返回值保存为它的初始 state。
- 当视图 首次渲染时,视图组件访问 Redux store 的当前 state,并使用该数据来决定要呈现的内容。同时监听 store 的更新,以便他们可以知道 state 是否已更改。
- 更新环节:
- 应用程序中发生了某些事情,例如用户单击按钮。
- dispatch 一个 action 到 Redux store,例如:
dispatch({type: 'counter/increment'})
。 - store 用之前的 state 和当前的 action 再次运行 reducer 函数,并将返回值保存为新的 state
- store 通知所有订阅过的视图,通知它们 store 发生更新。
- 每个订阅过 store 数据的视图 组件都会检查它们需要的 state 部分是否被更新。
- 发现数据被更新的每个组件都强制使用新数据重新渲染,紧接着更新网页。
action -> store.dispatch(action) -> reducer(state, action) -> store.getState()
。
动态展示图:
静态展示图(此图包含 redux 中间件,下文会说):
六、Redux 的中间件
1、Redux 中间件概述
为了理解中间件,让我们站在框架作者的角度思考问题:如果要添加功能,你会在哪个环节添加?
- Reducer:纯函数,只承担计算 State 的功能,不适合承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。
- View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。
- Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。
想来想去,只有发送 Action 的这个步骤,即 store.dispatch() 方法,可以添加其他功能。
Redux 中间件(Redux Middleware)就是一个函数。其本质是对 store.dispatch 方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。
Redux 中间件用于:在 dispatch 一个 action 之后,且在执行 reducer 之前,进行一些额外的操作。比如:日志记录、创建崩溃报告、调用异步接口或者路由等功能。
redux 的中间件函数一般都接收 2 个参数:dispatch 和 getState。
redux 的中间件函数一般都返回一个函数,该函数以 dispatch 为参数,该函数的返回值是接收 action 为参数的函数(可以看做另一个 dispatch 函数)。
在中间件链中,以 dispatch 为参数的函数的返回值将作为下一个中间件(准确的说应该是返回值)的参数,下一个中间件将它的返回值接着往下一个中间件传递,最终实现了 store.dispatch 在中间件间的传递。
redux 的中间件只关注 dispatch 函数的传递,至于在传递的过程中干了什么中间件并不关心。
3、使用 Redux 中间件进行 · 异步数据管理
Redux 只处理同步数据流,异步数据流交给其 “中间件” 处理。
七、Redux 的常见问题
八、在 React 中注册使用 Redux (进阶版)
1、React 中使用 Redux 的最佳实现
2、传统的方式
react-redux 提供了两个 API:Provider 和 Connect。
使用 Provider 和 Connect 将 Redux 绑定到 React 实例上。
(1)、Provider 组件
Provider 组件用来接受 Store,并且让 Store 对子组件可用。
例如:
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import App from './app';
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
这时候 Provider 组件的子组件 App 才可以使用 connect 方法关联 store。
【拓展】Provider 组件的现原理:
Provider 利用了 React 的 Context 特性,Context 用来传递一些父容器的属性对所有子孙组件可见,在某些场景下面避免了用 props 传递多层组件的繁琐。
(2)、Connect() 方法
Connect() 方法用来连接 React 组件与 Redux store,不会改变原来的组件类。
Connect() 方法可以接收 4 个可选的参数,这里简要概述一下,具体详见官网:
- [mapStateToProps(state, [ownProps]): stateProps]:该参数是一个函数,用来管理是否允许组件监听 Redux store 的变化。
- [mapDispatchProps(dispatch, [ownProps]): dispatchProps]:该参数可以是一个对象,也可以是一个函数,用来指定如何传递 dispatch 给组件。
- [mergeProps(stateProps, dispatchProps, ownProps): props]:该参数是一个函数,用来处理 mapStateToProps 和 mapDispatchProps 返回的结果。
- [options]:该参数是一个对象,用来定制 connector 的行为。
Connect() 方法会返回另一个函数,这个返回的函数用来接受一个组件类作为参数,最后它会返回一个和 Redux store 关联起来的新组件。
例如:
class App extends Component { ... }
export default connect()(App);
这样就可以在 App 这个组件里面通过 props 拿到 Store 的 dispatch 方法。
3、Hook 方式(★★★)
我们建议在 React 组件中使用 React Redux hooks API 作为默认方法。
现有的 connect API 仍然有效,并将继续受到支持,但hooks API更简单,与TypeScript一起使用效果更好。
(1)、在 React Redux 中使用 Hooks
与 connect() 一样,您应该首先将整个应用程序包装在Provider组件中,以使存储区在整个组件树中可用。
例如:
const store = createStore(rootReducer)
// As of React 18
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<Provider store={store}>
<App />
</Provider>
)
从这里,您可以导入列出的任何React-Redux挂钩API,并在函数组件中使用它们。
(2)、自定义上下文
Provider 组件允许您通过上下文属性指定备用上下文(动态的上下文)。如果您正在构建一个复杂的可重用组件,并且您不希望您的存储与消费者的应用程序可能使用的任何 Redux 存储相冲突,那么这是非常有用的。
要通过 hooks API 访问备用上下文,请使用 hook creator 函数:
import React from 'react'
import {
Provider,
createStoreHook,
createDispatchHook,
createSelectorHook,
} from 'react-redux'
const MyContext = React.createContext(null)
// 如果希望在其他文件中使用自定义挂钩,请导出它们。
export const useStore = createStoreHook(MyContext)
export const useDispatch = createDispatchHook(MyContext)
export const useSelector = createSelectorHook(MyContext)
const myStore = createStore(rootReducer)
export function MyProvider({ children }) {
return (
<Provider context={MyContext} store={myStore}>
{children}
</Provider>
)
}
(3)、组件渲染的性能优化
默认情况下,即使组件的属性没有更改,组件也会因其父级重新渲染而重新渲染。如何阻止这一情况的发生呢?
可以考虑将函数组件包装在 React.memo() 中,从而提升组件渲染的性能。
例如:
const CounterComponent = ({ name }) => {
const counter = useSelector((state) => state.counter)
return (
<div>
{name}: {counter}
</div>
)
}
export const MemoizedCounterComponent = React.memo(CounterComponent)
【推荐阅读】
阮一峰老师的三篇杰作:
Redux 入门教程(一):基本用法
Redux 入门教程(二):中间件与异步操作
Redux 入门教程(三):React-Redux 的用法
【参考文章】
redux 中文官网
React Redux 中文文档