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;
Related
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?
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?
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)
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>
)
I have redux-toolkit setup for managing app state. There is an 'authenticated' state variable that is false/true depending if the user is logged in. In the loginSuccessful reducer we set 'is_auth' in local storage and pull it from local storage in 'initialState'. In the login component we retrieve this variable from state to try and redirect based on the login being successful or not, but I can't seem to read the state variable after it is changed from the reducer and a redirect never happens...
here is the logic in login component
//app state
const authenticated = useSelector((state) => state.user.authenticated);
const error = useSelector((state) => state.user.error);
//form state
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const dispatch = useDispatch();
const handleLogin = (e) => {
e.preventDefault();
login({ email, password }, dispatch);
};
if (authenticated) return <Navigate to='/admin' />;
here is the reducer code:
const userSlice = createSlice({
name: 'user',
initialState: {
currentUser: null,
isLoading: false,
error: false,
authenticated: null || localStorage.getItem('is_auth'),
},
reducers: {
loginStart: (state) => {
state.isLoading = true;
state.error = false;
// state.authenticated = false;
},
loginSuccess: (state, action) => {
state.isLoading = false;
state.currentUser = action.payload;
localStorage.setItem('is_auth', true);
// state.authenticated = true;
},
loginFailure: (state) => {
state.isLoading = false;
state.error = true;
// state.authenticated = false;
},
logout: (state) => {
state.currentUser = null;
},
},
});
here is the login API request:
export const login = async (creds, dispatch) => {
dispatch(loginStart());
try {
const res = await apiRequest.post('auth/login', creds);
dispatch(loginSuccess(res.data));
} catch (err) {
dispatch(loginFailure());
}
};