Redux Toolkit中如何创建client文件并将请求数据存入state
Redux异步拉取数据存储操作指引
一、创建src/api/client请求工具
这个client本质是统一封装的HTTP请求工具,没有强制实现标准,只要支持Promise风格调用、返回结构和你thunk里的取值逻辑匹配即可。
- 先在src目录下新建
api文件夹,在文件夹内新建client.js文件 - 可以选择原生fetch封装(和官方示例逻辑完全对齐),代码如下:
// src/api/client.js export async function client(url, { method = 'GET', data, headers: customHeaders } = {}) { const headers = { 'Content-Type': 'application/json', ...customHeaders } const config = { method, headers } // 非GET请求序列化参数到请求体 if (method !== 'GET' && data) { config.body = JSON.stringify(data) } const res = await fetch(url, config) // 统一错误处理 if (!res.ok) { const errData = await res.json().catch(() => ({})) return Promise.reject({ status: res.status, ...errData }) } // 统一返回格式,对齐示例中response.data的取值逻辑 const resData = await res.json() return { data: resData } } // 绑定快捷调用方法 client.get = (url, config = {}) => client(url, { ...config, method: 'GET' }) client.post = (url, data, config = {}) => client(url, { ...config, method: 'POST', data }) client.put = (url, data, config = {}) => client(url, { ...config, method: 'PUT', data }) client.delete = (url, config = {}) => client(url, { ...config, method: 'DELETE' })
- 如果习惯用axios,也可以直接用axios生成实例作为client,不需要手写封装,先执行
npm install axios安装依赖,再写如下代码即可:
// src/api/client.js axios版本 import axios from 'axios' export const client = axios.create({ baseURL: '' // 可配置接口公共前缀,比如'/api' })
后续如果需要加token携带、请求响应拦截,直接在这个文件里统一修改即可。
二、将接口返回数据存入Redux state实现全局复用
createAsyncThunk执行时会自动派发三个状态的action:请求发起(pending)、请求成功(fulfilled)、请求失败(rejected),你只需要在slice的extraReducers里监听这三个action,把数据更新到state中即可,全应用所有组件都可以直接从store读取这份数据。
- 补全postsSlice的完整逻辑,核心是在extraReducers里处理成功回调,把接口返回值存入state:
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' import { client } from '../../api/client' const initialState = { posts: [], status: 'idle', // 状态枚举:idle(空闲未请求)/loading(请求中)/succeeded(请求成功)/failed(请求失败) error: null } // 异步thunk保持原有逻辑即可 export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => { const response = await client.get('/fakeApi/posts') return response.data }) const postsSlice = createSlice({ name: 'posts', initialState, reducers: { // 其他同步操作的reducer写在这里,比如新增、编辑、删除帖子 }, extraReducers(builder) { builder // 请求发起时更新状态为加载中 .addCase(fetchPosts.pending, (state) => { state.status = 'loading' }) // 请求成功时把返回数据存入state.posts .addCase(fetchPosts.fulfilled, (state, action) => { state.status = 'succeeded' // action.payload就是thunk中return的接口返回数据 // 全量覆盖场景直接赋值:state.posts = action.payload // 追加场景用concat:state.posts = state.posts.concat(action.payload) state.posts = action.payload }) // 请求失败时存储错误信息 .addCase(fetchPosts.rejected, (state, action) => { state.status = 'failed' state.error = action.error.message }) } }) export default postsSlice.reducer // 导出selector方便组件读取state,避免组件里重复写state路径 export const selectAllPosts = state => state.posts.posts export const selectPostsStatus = state => state.posts.status export const selectPostsError = state => state.posts.error
- 在store配置中注册该slice的reducer,保证posts节点挂载到根store上。
- 组件中触发请求、读取数据的示例:
import { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { fetchPosts, selectAllPosts, selectPostsStatus, selectPostsError } from './postsSlice' export default function PostsList() { const dispatch = useDispatch() const posts = useSelector(selectAllPosts) const status = useSelector(selectPostsStatus) const error = useSelector(selectPostsError) useEffect(() => { // 仅在空闲状态触发请求,避免重复拉取 if (status === 'idle') { dispatch(fetchPosts()) } }, [status, dispatch]) if (status === 'loading') return <div>帖子加载中...</div> if (status === 'failed') return <div>加载失败:{error}</div> return ( <div className="posts-list"> {posts.map(post => ( <div key={post.id} className="post-item"> <h3>{post.title}</h3> <p>{post.content}</p> </div> ))} </div> ) }
注意:Redux Toolkit内置了Immer,在reducer中直接修改state属性、调用数组的可变方法(push、concat等)都是安全的,不需要手动做不可变数据拷贝。
其他任何组件需要用到帖子数据时,只需要用useSelector(selectAllPosts)即可直接读取全局store中存储的帖子列表,不需要重复发起请求。
内容的提问来源于stack exchange,提问作者Sara Jalal
相关产品推荐
相关产品推荐

