I am building a blog app using react and redux toolkit. I have a problem when I am trying to delete the post, it is removed in my database and I got also the success message in the UI that the Redux toolkit pulls from MongoDB. But the problem is that when I click on the delete button the post is not removed right away and I need to manually reload the page so that it can be removed from the Ui. Can someone point out what am I doing wrong? Thanks. Here below is the logic I am doing:
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;
CodePudding user response:
you need to change following:
const deletePost = async (postId, token) => {
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.delete(API_URL postId, config);
if(response.data){
return postId;
}
};
Explanation: In your case, deletePost service was just returning the success message that resulted in action.payload to be not id of post in following:
.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);
})
this was causing the issue as your logic relies on filtering the deleted post id that should be provided by action.payload. In the above code I have just returned postId whenever deletePost is successful which provides correct action.payload to above case