Update Comments state in Redux Toolkit - reactjs

I'm creating a CRUD project with React and Redux toolkit and the problem is I can't update Comments after creating a new one.
I structured Comment Slice like that:
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import commentsService from "./commentService";
const initialState = {
comments: [],
isError: false,
isSuccess: false,
isLoading: false,
message: "",
};
export const createComment = createAsyncThunk(
"api/newcomment",
async (commentData, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
return await commentsService.createComment(commentData, token);
} catch (error) {
console.log(error);
}
}
);
export const getAllComments = createAsyncThunk(
"api/comments",
async (PostId, thunkAPI) => {
const token = thunkAPI.getState().auth.user.token;
return await commentsService.getAllComments(PostId, token);
}
);
export const deleteComment = createAsyncThunk(
"api/:id",
async (id, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
return await commentsService.deleteComment(id, token);
} catch (error) {
console.log(error);
}
}
);
export const commentsSlice = createSlice({
name: "comment",
initialState,
reducers: {
reset: (state) => initialState,
},
extraReducers: (builder) => {
builder
.addCase(createComment.pending, (state) => {
state.isLoading = true;
})
.addCase(createComment.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.comments = state.comments.push(action.payload);
})
.addCase(createComment.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(getAllComments.pending, (state) => {
state.isLoading = true;
})
.addCase(getAllComments.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
const comments = state.comments.filter(
(comment) => comment.PostId === action.payload.PostId
);
if (comments.length === 0) {
state.comments.push(action.payload);
}
})
.addCase(getAllComments.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(deleteComment.pending, (state) => {
state.isLoading = true;
})
.addCase(deleteComment.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.comments = state.comments.filter(
(comment) => comment.id !== action.payload.id
);
})
.addCase(deleteComment.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
});
},
});
And CardPost where I dispatch comments:
const CardPost = ({ post }) => {
const [show, setShow] = useState(false);
const dispatch = useDispatch();
const { user } = useSelector((state) => state.auth);
const PostId = post.id;
const { comments, isLoading, isError, message } = useSelector(
(state) => state.comments
);
useEffect(() => {
if (isError) {
console.log(message);
}
dispatch(getAllComments(PostId));
}, [dispatch, PostId, isError, message]);
//// Show Comments
<div>
<div>
<FormComment PostId={PostId} />
</div>
<div>
{comments.length > 0 ? (
<div className="comments">
{comments.map((id) => (
<div key={id}>
<CardComment post={post} />
</div>
))}
</div>
) : (
<Typography>No Comment..</Typography>
)}
</div>
</div>
When there are several comments on a Post that are displayed and I create a new one, it shows me the message "No comment.." but all the messages appear well when the page is refreshed.
Where did I make it wrong?
Is it the .addCase that I built wrong and the state is not updated or is it the useEffect dispatch?

Related

rejectWithValue createAsyncThunk

I want to create createAsyncThunk function for register user but there is a problem when rejected action, it is throw an error
the code of authSlice:
`
export const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
reset: (state) => {
state.isLoading = false;
state.isSuccess = false;
state.isError = false;
state.message = "";
},
},
extraReducers: (builder) => {
builder
.addCase(register.pending, (state) => {
state.isLoading = true;
})
.addCase(register.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.user = action.payload;
})
.addCase(register.rejected, (state, action) => {
console.log({ action });
state.isLoading = false;
state.isError = true;
state.message = action.payload;
state.user = null;
})
},
});
the code of function register user
// Register user
export const register = createAsyncThunk(
"auth/register",
async (user, thunkAPI) => {
const { rejectWithValue } = thunkAPI;
try {
return await authService.register(user);
} catch (error) {
const message = error.message;
return rejectWithValue(message);
}
}
);
import axios from "axios";
const API_URL = "http://localhost:5000/auth/condidate";
const register = async (userData) => {
const response = await axios.post(API_URL, userData);
if (response.data) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
};
const authService = {
register,
};
export default authService;
`
there is no problem with register fulfilled but rejected regisiter throw an error
enter image description here
try to register user with react and redux toolkit but there is a problem with reject the action of register

Unable to display the data into textfield using redux toolkit

I'm trying to update my data but them problem is unable to display the data into Texfield. But in console log or network the data are display. Please see below my code.
Please anyone help. Thanks
// Update Form
const { id } = useParams();
const dispatch = useDispatch();
const userInfo = useSelector(selectUser);
const [userData, setUserData] = useState(userInfo);
useEffect(() => {
dispatch(getUser(id));
}, [dispatch, id]);
useEffect(() => {
if (userInfo) {
setUserData(userInfo);
}
}, [userInfo]);
<TextField name="name" value={userInfo?.name || ''} />
<TextField name="email" value={userInfo?.email|| ''} />
// User Slice
const userSlice = createSlice({
name: 'user',
initialState: userInfo: null,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(getUser.pending, (state) => {
state.isLoading = true;
})
.addCase(getUser.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.isError = false;
state.userInfo = action.payload;
})
.addCase(getUser.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
toast.error(action.payload);
})
export const selectUser = (state) => state.user.userInfo;

useEffect goes to cyclic dependency with Redux Toolkit

Tryed to render component using useEffect with "product" dependency but it goes cyclic dependency.
Tryed to use prev state but it doesn't help.
React don't gives any error, but useEffect send request every second.
Component:
export const ProductPage = (props: any) => {
const { product, isLoading, error } = useTypedSelector(state => state.Product)
const dispatch = useTypedDispatch()
const { id } = useParams()
const prevProd = usePrevious(product)
useEffect(() => {
if (prevProd !== product){dispatch(fetchProduct(Number(id)))}
}, [product])
return (
<div>
<div>{product.name}</div>
<div>{product.id}</div>
<div>{product.price}</div>
</div>
)
}
Async Thunk:
export const fetchProduct = createAsyncThunk(
'product/fetch',
async (id: number, thunkApi) => {
try {
const response = await productService.fetch(id)
return response.data
} catch (error: any) {
return thunkApi.rejectWithValue(error.message)
}
}
)
Slice:
export const ProductSlice = createSlice({
name: 'product',
initialState,
reducers: {},
extraReducers: {
[fetchProduct.fulfilled.type]: (state, action: PayloadAction<IProductData>) => {
state.error = ''
state.isLoading = false
state.product = action.payload
},
[fetchProduct.pending.type]: (state) => {
state.isLoading = true
},
[fetchProduct.rejected.type]: (state, action: PayloadAction<string>) => {
state.isLoading = false
state.error = action.payload
},
}
})
Explain please, why this problem occured and how resolve this?

Why useEffect works only after manual reload?

I use react redux redux-toolkit
When switching to CardDetails, everything works and I get data in the state of only one element, but when I go from this page to any other page that has useEffect and in which I use the same state, it does not work and I have to reload the page manually. For what reason can this happen?
Here is the page code for one element
const CardDetails = () => {
const {id} = useParams()
const dispatch = useDispatch()
const location = useLocation()
const {posts, loading} = useSelector((state) => ({...state.posts}))
useEffect(() => {
dispatch(getPost(id))
}, []);
console.log(posts)
return (
<div>
<img style={{maxWidth: "150px"}} src={posts.img} alt="image"/>
</div>
);
};
Here is the page I go to from CardDetails
const Main = () => {
const dispatch = useDispatch()
const {posts, loading} = useSelector((state) => ({...state.posts}))
const user = JSON.parse(localStorage.getItem('profile'))
useEffect(() => {
dispatch(getPosts())
}, []);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [creator, setCreator] = useState("");
const [category, setCategory] = useState("");
const [img, setImg] = useState("");
const [tags, setTags] = useState(["tag1", "tag2"]);
const onSavePostClicked = () =>{
const newPost = {title, description, category, img}
dispatch(createPost(newPost))
setTitle('')
setDescription('')
setCreator('')
setCategory('')
setImg('')
}
return (
And here is the postsSlice
import {createAsyncThunk, createSlice} from "#reduxjs/toolkit";
import * as api from "../../../api/index";
import {fetchPost, fetchPostsCategory} from "../../../api/index";
export const getPosts = createAsyncThunk(
'posts/getPosts',
async (thunkAPI)=> {
const response = await api.fetchPosts()
return response.data.data
})
export const getPost = createAsyncThunk(
'posts/getPost',
async (id,thunkAPI)=> {
const response = await api.fetchPost(id)
return response.data.data
})
export const createPost = createAsyncThunk(
'posts/createPosts',
async (newPost,thunkAPI)=> {
const response = await api.createPost(newPost)
return response.data.data
})
export const deletePost = createAsyncThunk(
'posts/deletePost',
async (post,thunkAPI)=> {
const {_id} = post
const response = await api.deletePost(_id)
return response.data
})
export const updatePost = createAsyncThunk(
'posts/updatePost',
async (post,thunkAPI)=> {
const {_id} = post
const postEdited = {...post, published: true}
const response = await api.updatePost(_id, postEdited)
return response.data
})
export const getCategory = createAsyncThunk(
'posts/getCategory',
async (category,thunkAPI)=> {
const response = await api.fetchPostsCategory(category)
return response.data.data
})
const initialState = {
posts: [],
loading: false,
error:null
}
export const postsSlice = createSlice({
name: "posts",
initialState,
reducers:{
postAdded: {
reducer(state, action) {
state.posts.push(action.payload)
},
prepare(title, description, creator, category, img) {
return {
payload: {
title,
description,
creator,
category,
img
}
}
}
},
},
extraReducers: {
[createPost.pending]: (state, action) => {
state.loading = true;
},
[createPost.fulfilled]: (state, action) => {
state.loading = false;
state.posts.push(action.payload);
},
[createPost.rejected]: (state, action) => {
state.loading = false;
state.error = action.payload.message;
},
[getPosts.pending]: (state) => {
state.loading = true
},
[getPosts.fulfilled]: (state, action) => {
state.loading = false
state.posts = action.payload
},
[getPosts.rejected]: (state) => {
state.loading = false
},
[deletePost.fulfilled]: (state, action)=>{
if (!action.payload?._id) {
console.log('Delete could not complete')
console.log(action.payload)
return;
}
const { _id } = action.payload;
state.posts = state.posts.filter(post => post._id !== _id);
},
[updatePost.pending]: (state, action) => {
state.loading = true;
},
[updatePost.fulfilled]: (state, action) => {
const { _id } = action.payload;
state.loading = false;
const posts = state.posts.filter(post => post._id !== _id);
state.posts = [...posts, action.payload];
},
[updatePost.rejected]: (state, action) => {
state.loading = false;
state.error = action.payload.message;
},
[getCategory.pending]: (state) => {
state.loading = true
},
[getCategory.fulfilled]: (state, action) => {
state.loading = false
console.log()
state.posts = action.payload
},
[getCategory.rejected]: (state) => {
state.loading = false
},
[getPost.pending]: (state) => {
state.loading = true
},
[getPost.fulfilled]: (state, action) => {
state.loading = false
state.posts = action.payload
},
[getPost.rejected]: (state) => {
state.loading = false
},
},
})
export const {postAdded} = postsSlice.actions
export default postsSlice.reducer
When you are using useEffect with an empty array as deps [] it will only be trigger when you mount your component (and cleaned when you unmount the component)

ReactJS - infinite loop when dispatch the redux action

i got some problem for a couple hours, i really dont know why useeffect give me infinite loop when i dispatch the redux action ? because i want my page is will refresh after i change the value from order state. is there not allowed to put the async state from redux in useeffect? sorry im still new..
orderSlice.js
export const orderPay = createAsyncThunk(
'order/orderPay',
async (order, thunkAPI) => {
try {
const user1 = thunkAPI.getState().user.userInfo;
const config = {
headers: {
'Content-Type': 'Application/json',
Authorization: `Bearer ${user1.token}`,
},
};
const res = await axios.put(
`http://localhost:8000/api/order/${order.orderId}/pay`,
order.paymentResult,
config
);
const data = await res.data;
return data;
} catch (error) {
thunkAPI.rejectWithValue(
error.response && error.response.data.message
? error.response.data.message
: error.response
);
}
}
);
const orderSlice = createSlice({
name: 'order',
initialState: {
loading: false,
error: '',
order: {},
success: false,
},
reducers: {
orderDetail: (state, action) => {
state.order = action.payload;
},
orderPayReset: (state, action) => {
state.loading = false;
state.error = '';
state.order = {};
state.success = false;
},
},
extraReducers: (builder) => {
builder
.addCase(postOrder.pending, (state, action) => {
state.loading = true;
state.order = {};
state.success = false;
})
.addCase(postOrder.fulfilled, (state, action) => {
state.loading = false;
state.order = action.payload;
state.success = true;
})
.addCase(postOrder.rejected, (state, action) => {
state.error = action.payload;
})
.addCase(orderDetail.pending, (state, action) => {
state.loading = true;
state.order = {};
state.success = false;
})
.addCase(orderDetail.fulfilled, (state, action) => {
state.loading = false;
state.order = action.payload;
state.success = true;
})
.addCase(orderDetail.rejected, (state, action) => {
state.error = action.payload;
})
.addCase(orderPay.pending, (state, action) => {
state.loading = true;
state.success = false;
})
.addCase(orderPay.fulfilled, (state, action) => {
state.loading = false;
state.success = true;
})
.addCase(orderPay.rejected, (state, action) => {
state.error = action.payload;
});
},
});
OrderScreen.jsx
const OrderScreen = () => {
const navigate = useNavigate();
const { orderId } = useParams();
const [sdkReady, setSdkReady] = useState(false);
const { paymentMethod } = useSelector((state) => state.cart);
const { userInfo } = useSelector((state) => state.user);
const { success, loading, error, order, orderPaid } = useSelector(
(state) => state.order
);
const dispatch = useDispatch();
useEffect(() => {
const addPaypalScript = async () => {
const res = await axios.get(`http://localhost:8000/api/config/paypal`);
const data = await res.data;
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = `https://www.paypal.com/sdk/js?client-id=${data}`;
script.async = true;
script.onload = () => {
setSdkReady(true);
};
document.body.appendChild(script);
return data;
};
(async () => await addPaypalScript())();
if (!order || success) {
dispatch(orderPayReset());
dispatch(orderDetail(orderId));
} else if (!order.isPaid) {
if (!window.paypal) {
addPaypalScript();
} else {
setSdkReady(true);
}
}
}, [orderId, dispatch]);
const successPaymentHandler = (paymentResult) => {
dispatch(orderPay({ orderId, paymentResult }));
order.isPaid = true;
};
// console.log(orderPaid);
return loading ? (
<h1>Order {order._id}</h1>
)

Resources