React+Redux技术问题:如何展示JSON placeholder的全部帖子
问题分析与修复方案
核心问题点
getPostAll异步Thunk未正确返回数据:你的getPostAll中最后一个.then()调用了console.log,而console.log返回undefined,导致Redux无法接收到帖子列表数据。- 未处理
getPostAll的Redux状态更新:extraReducers中没有为getPostAll的pending/fulfilled/rejected状态编写处理逻辑,获取到的数据无法存入Redux store。 - 组件中错误的dispatch调用方式:在JSX渲染阶段直接调用
dispatch(getPostAll())会导致重复触发请求,且没有从store中读取数据进行渲染,而是试图直接遍历dispatch的返回值(promise对象,无法forEach)。 - 单个帖子与列表的状态存储冲突:现有代码中所有异步操作完成后都将
state.post设为[action.payload],但获取全部帖子时应该直接将数组赋值给state.post。
修复后的代码
1. 修正PostSlice.js
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit" export const getPost = createAsyncThunk("post/getPost", async ({ id }) => { const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`); return res.json(); }) // 修复:移除console.log的then链,直接返回解析后的JSON数据 export const getPostAll = createAsyncThunk("posts/getPosts", async () => { const res = await fetch(`https://jsonplaceholder.typicode.com/posts`); const data = await res.json(); console.log(data, typeof data, data[0].id); // 调试日志可以保留,但不要作为返回值 return data; // 必须返回数据给Redux }) export const deletePost = createAsyncThunk( "post/deletePost", async ({ id }) => { const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, { method: "DELETE", }); return res.json(); } ) export const createPost = createAsyncThunk( "post/createPost", async ({ values }) => { const res = await fetch(`https://jsonplaceholder.typicode.com/posts`, { method: "POST", headers: { Accept: "application/json", "Content-type": "application/json", }, body: JSON.stringify({ title: values.title, body: values.body, }), }); return res.json(); } ) export const updatePost = createAsyncThunk( "post/updatePost", async ({ id, title, body }) => { const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, { method: "PUT", headers: { Accept: "application/json", "Content-type": "application/json", }, body: JSON.stringify({ title, body, }), }); return res.json(); } ) const PostSlice = createSlice({ name: "post", initialState: { posts: [], // 重命名为posts更语义化,区分单个帖子和列表 loading: false, error: null, body: "", edit: false, }, reducers: { setEdit: (state, action) => { state.edit = action.payload.edit; state.body = action.payload.body; }, }, extraReducers: (builder) => { // 使用builder API(Redux Toolkit推荐写法) // 处理单个帖子请求 builder .addCase(getPost.pending, (state) => { state.loading = true; }) .addCase(getPost.fulfilled, (state, action) => { state.loading = false; state.posts = [action.payload]; // 单个帖子存入数组 }) .addCase(getPost.rejected, (state, action) => { state.loading = false; state.error = action.error.message; // 捕获错误信息 }) // 处理全部帖子请求 .addCase(getPostAll.pending, (state) => { state.loading = true; }) .addCase(getPostAll.fulfilled, (state, action) => { state.loading = false; state.posts = action.payload; // 直接赋值数组 }) .addCase(getPostAll.rejected, (state, action) => { state.loading = false; state.error = action.error.message; }) // 处理删除帖子 .addCase(deletePost.pending, (state) => { state.loading = true; }) .addCase(deletePost.fulfilled, (state, action) => { state.loading = false; // 删除后过滤掉对应id的帖子 state.posts = state.posts.filter(post => post.id !== action.meta.arg.id); }) .addCase(deletePost.rejected, (state, action) => { state.loading = false; state.error = action.error.message; }) // 处理创建帖子 .addCase(createPost.pending, (state) => { state.loading = true; }) .addCase(createPost.fulfilled, (state, action) => { state.loading = false; state.posts.unshift(action.payload); // 新增帖子放到列表开头 }) .addCase(createPost.rejected, (state, action) => { state.loading = false; state.error = action.error.message; }) // 处理更新帖子 .addCase(updatePost.pending, (state) => { state.loading = true; }) .addCase(updatePost.fulfilled, (state, action) => { state.loading = false; // 找到对应id的帖子并更新 const index = state.posts.findIndex(post => post.id === action.payload.id); if (index !== -1) { state.posts[index] = action.payload; } }) .addCase(updatePost.rejected, (state, action) => { state.loading = false; state.error = action.error.message; }); }, }) export const { setEdit } = PostSlice.actions; export default PostSlice.reducer;
2. 修正Post.js组件
import React, { useEffect } from "react" import { useDispatch, useSelector } from "react-redux" import { getPostAll } from "../redux/features/PostSlice" const Posts = () => { // 补全组件函数声明 const dispatch = useDispatch(); // 从Redux store中获取数据 const { posts, loading, error } = useSelector(state => state.post); // 组件挂载时触发获取全部帖子的请求 useEffect(() => { dispatch(getPostAll()); }, [dispatch]); if (loading) { return <div className="text-center mt-4">加载中...</div>; } if (error) { return <div className="text-center mt-4 text-danger">加载失败:{error}</div>; } return ( <> <div className="row mt-4 d-flex align-items-center justify-content-center"> <h2>Dashboard</h2> </div> <div className="mt-4"> <h2 className="text-center">All Posts</h2> <div className="container mt-4"> <div className="row g-4"> {/* 遍历posts数组渲染每个帖子 */} {posts.map(post => ( <div key={post.id} className="col-md-6"> <div className="card h-100"> <div className="card-body"> <h5 className="card-title">{post.title}</h5> <p className="card-text">{post.body}</p> </div> </div> </div> ))} </div> </div> </div> </> ) } export default Posts
关键说明
- 异步Thunk返回值:异步Thunk必须返回要存入store的数据,不能返回
console.log这类无意义的值。 - Redux状态更新:使用Redux Toolkit推荐的
builderAPI处理extraReducers,确保每个异步操作的状态都被正确处理。 - 组件数据获取:通过
useEffect在组件挂载时触发数据请求,避免渲染阶段重复调用dispatch;通过useSelector从store中读取数据,再用map渲染列表(forEach不会返回JSX元素,必须用map)。 - 状态语义化:将
state.post重命名为state.posts,更清晰地表示这是帖子列表。
内容的提问来源于stack exchange,提问作者Jeevan Lenka
相关产品推荐
相关产品推荐

