安装 Redux Toolkit 和 React-Redux
添加 Redux Toolkit 和 React-Redux 依赖包到你的项目中:
npm install @reduxjs/toolkit react-redux
创建 Redux Store
创建 src/store/index.js 文件。从 Redux Toolkit 引入 configureStore API。我们从创建一个空的 Redux store 开始,并且导出它:
// src/store/index.js
import { configureStore } from '@reduxjs/toolkit'
export default configureStore({
reducer: {}
})
为 React 注入 Redux Store
创建 store 后,便可以在 React 组件中使用它。 在 src/index.js 中引入我们刚刚创建的 store , 通过 React-Redux 的 <Provider>将 <App> 包裹起来,并将 store 作为 prop 传入。
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import store from './store/index.js'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
创建 Redux State Slice
创建 src/store/modules/counterStore.js 文件。在该文件中从 Redux Toolkit 引入 createSlice API。
创建 slice 需要一个字符串名称来标识切片、一个初始 state 以及一个或多个定义了该如何更新 state 的 reducer 函数。slice 创建后 ,我们可以导出 slice 中生成的 Redux action creators 和 reducer 函数。
// src/store/modules/counterStore.js
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: {
value: 0
},
reducers: {
increment: state => {
// Redux Toolkit 允许我们在 reducers 写 "可变" 逻辑。它
// 并不是真正的改变状态值,因为它使用了 Immer 库
// 可以检测到“草稿状态“ 的变化并且基于这些变化生产全新的
// 不可变的状态
state.value += 1
},
decrement: state => {
state.value -= 1
},
// 第二个参数action的payload属性接收来自组件中dispatch action的传参
incrementByAmount: (state, action) => {
state.value += action.payload
}
}
})
// 每个 case reducer 函数会生成对应的 Action creators
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
将 Slice Reducers 添加到 Store 中
下一步,我们需要从计数切片中引入 reducer 函数,并将它添加到我们的 store 中。通过在 reducer 参数中定义一个字段,我们告诉 store 使用这个 slice reducer 函数来处理对该状态的所有更新。
// src/store/index.js
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export default configureStore({
reducer: {
counter: counterReducer
}
})
在 React 组件中使用 Redux 状态和操作
现在我们可以使用 React-Redux 钩子让 React 组件与 Redux store 交互。我们可以使用 useSelector 从 store 中读取数据,使用 useDispatch dispatch actions。
import { useSelector, useDispatch } from 'react-redux';
import {
decrement,
increment,
incrementByAmount,
} from './store/modules/counterStore.js';
export default function ReduxCounter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<button onClick={() => dispatch(decrement())}>-</button>
{count}
<button onClick={() => dispatch(increment())}>+</button>
{/* dispatch action传递的参数会在reducer函数的第二个参数action.payload进行接收 */}
<button onClick={() => dispatch(incrementByAmount(10))}>add 10</button>
</div>
);
}
用 Thunk 编写异步逻辑
到目前为止,我们应用程序中的所有逻辑都是同步的。首先 dispatch action,store 调用 reducer 来计算新状态,然后 dispatch 函数完成并结束。但是,JavaScript 语言有很多编写异步代码的方法,我们的应用程序通常具有异步逻辑,比如从 API 请求数据之类的事情。我们需要一个地方在我们的 Redux 应用程序中放置异步逻辑。
thunk 是一种特定类型的 Redux 函数,可以包含异步逻辑。Thunk 是使用两个函数编写的:
- 一个内部 thunk 函数,它以 dispatch 和 getState 作为参数
- 外部创建者函数,它创建并返回 thunk 函数
比如我们从 counterStore 导出一个函数incrementAsync,incrementAsync就是一个 thunk action creator。
// 下面这个函数就是一个 thunk ,它使我们可以执行异步逻辑
// 你可以 dispatched 异步 action `dispatch(incrementAsync(10))` 就像一个常规的 action
// 调用 thunk 时接受 `dispatch` 函数作为第一个参数
// 当异步代码执行完毕时,可以 dispatched actions
export const incrementAsync = amount => dispatch => {
setTimeout(() => {
dispatch(incrementByAmount(amount))
}, 1000)
}
我们可以像使用普通 Redux action creator 一样使用它们:
store.dispatch(incrementAsync(5))
但是,使用 thunk 需要在创建时将 redux-thunk middleware(一种 Redux 插件)添加到 Redux store 中。幸运的是,Redux Toolkit 的 configureStore 函数已经自动为我们配置好了,所以我们可以继续在这里使用 thunk。
当你需要进行 AJAX 调用以从服务器获取数据时,你可以将该调用放入 thunk 中。这是一个写得有点长的例子,所以你可以看到它是如何定义的:
// 外部的 thunk creator 函数
const fetchUserById = userId => {
// 内部的 thunk 函数
return async (dispatch, getState) => {
try {
// thunk 内发起异步数据请求
const user = await userAPI.fetchById(userId)
// 但数据响应完成后 dispatch 一个 action
dispatch(userLoaded(user))
} catch (err) {
// 如果过程出错,在这里处理
}
}
}
dispatch thunk action creator
useEffect(() => {
dispatch(fetchUserById(5));
}, [dispatch]);
总结
我们可以使用 Redux Toolkit configureStore API 创建一个 Redux store
- configureStore 接收 reducer 函数来作为命名参数
- configureStore 自动使用默认值来配置 store
在 slice 文件中编写 Redux 逻辑
- 一个 slice 包含一个特定功能或部分的 state 相关的 reducer 逻辑和 action
- Redux Toolkit 的 createSlice API 为你提供的每个 reducer 函数生成 action creator 和 action 类型
Redux reducer 必须遵循以下原则
- 必须依赖 state 和 action 参数去计算出一个新 state
- 必须通过拷贝旧 state 的方式去做 不可变更新 (immutable updates)
- 不能包含任何异步逻辑或其他副作用
- Redux Toolkit 的 createSlice API 内部使用了 Immer 库才达到表面上直接修改("mutating")state 也实现不可变更新(immutable updates)的效果
一般使用 “thunks” 来开发特定的异步逻辑
- Thunks 接收 dispatch 和 getState 作为参数
- Redux Toolkit 内置并默认启用了 redux-thunk 中间件
使用 React-Redux 来做 React 组件和 Redux store 的通信
- 在应用程序根组件包裹
<Provider store={store}>使得所有组件都能访问到 store - 全局状态应该维护在 Redux store 内,局部状态应该维护在局部 React 组件内