React+Redux Toolkit删除文章后UI未自动更新问题排查
问题描述
我用React和Redux Toolkit开发博客应用,删除文章时,数据库里已经移除目标内容,Redux Toolkit也收到了MongoDB返回的成功消息,但点击删除按钮后文章不会立刻从UI消失,必须手动刷新页面才行。请帮忙找出问题所在。
实现代码
postSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import postService from './postService'; const initialState = { posts: [], isError: false, isSuccess: false, isLoading: false, message: '', }; // Create a post export const createPost = createAsyncThunk( 'posts/create', async (postData, thunkAPI) => { try { const token = thunkAPI.getState().auth.user.token; return await postService.createPost(postData, token); } catch (error) { const message = (error.response && error.response.data && error.response.data.message) || error.message || error.toString(); return thunkAPI.rejectWithValue(message); } } ); // Get all posts export const getPosts = createAsyncThunk( 'posts/getAll', async (_, thunkAPI) => { try { return await postService.getPosts(); } catch (error) { const message = (error.response && error.response.data && error.response.data.message) || error.message || error.toString(); return thunkAPI.rejectWithValue(message); } } ); // Delete post export const deletePost = createAsyncThunk( 'posts/delete', async (id, thunkAPI) => { try { const token = thunkAPI.getState().auth.user.token; return await postService.deletePost(id, token); } catch (error) { const message = (error.response && error.response.data && error.response.data.message) || error.message || error.toString(); return thunkAPI.rejectWithValue(message); } } ); export const postSlice = createSlice({ name: 'post', initialState, reducers: { reset: (state) => initialState, }, extraReducers: (builder) => { builder .addCase(createPost.pending, (state) => { state.isLoading = true; }) .addCase(createPost.fulfilled, (state, action) => { state.isLoading = false; state.isSuccess = true; state?.posts.push(action.payload); }) .addCase(createPost.rejected, (state, action) => { state.isLoading = false; state.isError = true; state.message = action.payload; }) .addCase(getPosts.pending, (state) => { state.isLoading = true; }) .addCase(getPosts.fulfilled, (state, action) => { state.isLoading = false; state.isSuccess = true; state.posts = action.payload; }) .addCase(getPosts.rejected, (state, action) => { state.isLoading = false; state.isError = true; state.message = action.payload; }) .addCase(deletePost.pending, (state) => { state.isLoading = true; }) .addCase(deletePost.fulfilled, (state, action) => { state.isLoading = false; state.isSuccess = true; state.posts = state.posts.filter((post) => post._id !== action.payload); console.log(action.payload); }) .addCase(deletePost.rejected, (state, action) => { state.isLoading = false; state.isError = true; state.message = action.payload; }); }, }); export const { reset } = postSlice.actions; export default postSlice.reducer;
postService.js
import axios from 'axios'; const API_URL = '/api/posts/'; // Create a post const createPost = async (postData, token) => { const config = { headers: { Authorization: `Bearer ${token}`, }, }; const response = await axios.post(API_URL, postData, config); return response.data; }; // Get all posts const getPosts = async () => { const response = await axios.get(API_URL); return response.data; }; // Delete a post const deletePost = async (postId, token) => { const config = { headers: { Authorization: `Bearer ${token}`, }, }; const response = await axios.delete(API_URL + postId, config); return response.data; }; const postService = { createPost, getPosts, deletePost, }; export default postService;
Post.js
import React from 'react'; import './post.css'; import { FaSmileBeam, FaRegSmile } from 'react-icons/fa'; import { RiEdit2Fill } from 'react-icons/ri'; import { MdDeleteOutline } from 'react-icons/md'; import moment from 'moment'; import { useDispatch } from 'react-redux'; import { deletePost, } from '../../features/posts/postSlice'; const Post = ({ post }) => { const dispatch = useDispatch(); const user = JSON.parse(localStorage.getItem('user')); return ( <> <FaRegSmile className="smile__icon" /> </> ); }; const removePost = () => { dispatch(deletePost(post._id)); }; const { title, createdAt, body, imageFile, postCreator, author } = post; return ( <div className="post__container card"> <div className="post__card"> <div className="post__image"> <img src={imageFile} alt="post" /> </div> <div className="post__content"> <div className="edit__icon"> {author === user?.result?._id && ( <RiEdit2Fill className="edit__icon" fontSize="small" /> )} </div> <div className="post__header__title"> <h3>{title}</h3> </div> <div className="post__message"> <p>{body}</p> </div> <div className="post__header"> <div className="post__header__date"> {user ? ( <span className="user__avatar"> <p>{user?.result?.username.charAt(0).toUpperCase()}</p> </span> ) : ( '' )} <span className="post__header__date__text"> {postCreator.toUpperCase()} <p className="moments">{moment(createdAt).fromNow()}</p> </span> </div> </div> <div className="post__actions"> <div className="icon__box"> <Smile disabled={!user} /> </div> <div className="icon__box"> <MdDeleteOutline className="delete__icon" onClick={removePost} /> </div> </div> </div> </div> </div> ); }; export default Post;
问题分析与解决方案
核心问题
UI不自动更新的原因是Redux状态更新逻辑不匹配:
postService.deletePost返回的是后端接口的成功消息(比如字符串"删除成功"),但在postSlice的deletePost.fulfilled回调里,你用action.payload(也就是这个成功消息)去和文章的_id做过滤匹配,条件永远不成立,导致posts数组没有被修改,UI自然不会更新。
修复方案
方案一:修改Redux异步操作返回值
直接在deletePost的异步逻辑里返回传入的文章ID,而不是后端返回的消息:
export const deletePost = createAsyncThunk( 'posts/delete', async (id, thunkAPI) => { try { const token = thunkAPI.getState().auth.user.token; await postService.deletePost(id, token); return id; // 直接返回文章ID,替代后端返回的消息 } catch (error) { const message = (error.response && error.response.data && error.response.data.message) || error.message || error.toString(); return thunkAPI.rejectWithValue(message); } } );
这样action.payload就是被删除文章的ID,filter逻辑能正确过滤掉目标文章,UI会自动更新。
方案二:修改后端接口
让删除接口返回被删除文章的ID,这样postService.deletePost返回的就是ID,原有的Redux状态更新逻辑无需修改。
额外修复点
Post组件里有一段多余的代码会导致渲染异常,需要删除:
// 删掉这段无效代码 return ( <> <FaRegSmile className="smile__icon" /> </> ); };
另外组件中用到的<Smile />是未定义的,需要替换为FaRegSmile或FaSmileBeam,避免报错。
内容的提问来源于stack exchange,提问作者saygas
相关产品推荐
相关产品推荐

