My code flow is that a user fills the form and submits it which goes to action calling the API and then it returns the postId
and stores it in the reducer. Now my main React component has useSelector
to get the latest state for the postId
. So after calling the action and submitting the form I am doing the navigate to that post through '/classifieds/{postId}'
but postId
shows null inside the handleSubmit
function whereas it shows the value of postId
outside that function. So probably my way of thinking is wrong. Can someone help me in suggesting how should be the flow?
classifieds.js
import React, { useEffect, useState } from "react";
import { TextEditor } from "../../../components/Text-Editor/text-editor";
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
import InputGroup from 'react-bootstrap/InputGroup';
import { useDispatch, useSelector } from "react-redux";
import { getCategories, postClassifieds } from "../../../redux/actions/classifiedsAction";
import './post-classified.scss'
import { useNavigate } from "react-router-dom";
export const PostClassified = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const classifiedsLoading = useSelector((state) => state.classifieds.loading);
const postId = useSelector((state) => state.classifieds.postId);
console.log("postID is 1",postId) // this shows up with the id when handleSubmit is called
const handleInputChange = (e) => {
const { name, value, type, checked } = e.target;
const inputValue = type === 'checkbox' ? checked : value;
setFormData({ ...formData, [name]: inputValue });
};
const [formData, setFormData] = useState({
heading: '',
category:''
});
const [formSubmitMessage, setFormSubmitMessage] = useState(false);
const newFormData = new FormData();
const handleSubmit = async (e) => {
e.preventDefault();
setFormSubmitMessage(false);
newFormData.append("heading",formData.heading);
newFormData.append("category",formData.category);
await dispatch(postClassifieds(newFormData));
console.log("postID is 2",postId) // this shows up null
navigate(`/classifieds/${postId}`)
};
return (
<div className="section properties">
<div className="container">
{classifiedsLoading
? (<></>)
: <Form onSubmit={handleSubmit} encType="multipart/form-data" className="form-post">
{/* form.............. */}
</Form>
}
</div>
</div>
);
}
classifiedsAction.js
import axios from "axios";
export const POST_SUCCESS = "POST_SUCCESS";
export const POST_FAILURE = "POST_FAILURE";
export const CLASSIFIEDS_LOADING = "CLASSIFIEDS_LOADING";
// post classified
export const postClassifieds = (formData) => async(dispatch) => {
try {
const response = await axios.post('/posts', formData);
dispatch({
type: 'POST_SUCCESS',
payload: response.data._id, // Assuming the server returns the new document's ID
});
} catch(error) {
console.log("error is", error);
dispatch({
type: 'POST_FAILURE',
payload: error.message,
});
}
};
// Classifieds loading
export const setClassifiedsLoading = () => {
return {
type: CLASSIFIEDS_LOADING,
};
};
classifiedsReducer.js
import {
CLASSIFIEDS_LOADING,
POST_SUCCESS,
POST_FAILURE
} from "../actions/classifiedsAction";
const initialState = {
loading: true,
postID: null,
error: null
};
export const ClassifiedsReducer = (state = initialState, action) => {
switch (action.type) {
case POST_SUCCESS:
return {
...state,
postId: action.payload,
loading: false,
};
case POST_FAILURE:
return {
...state,
error: action.payload,
loading: false,
};
case CLASSIFIEDS_LOADING:
return {
...state,
loading: true,
};
default:
return state;
}
};
2
Answers
The issue you’re facing is likely due to the asynchronous nature of the dispatch(postClassifieds(newFormData)) call. The postId value is updated in the Redux store after the asynchronous API call is complete, but the console.log("postID is 2", postId) and the subsequent navigate(/classifieds/${postId}) are executed before the Redux state is updated, leading to postId being null.
To solve this, you can utilize the useEffect hook to listen for changes in the postId and trigger the navigation when it is updated. Here’s a modified version of your PostClassified component:
related : overflow
The issue here is that the submit handler has a stale closure over the selected
postId
state.Update the code such that your
postClassifieds
action returns a resolved value to the calling code that can be awaited.Example:
Wrap the asynchronous logic in a
try/catch
to handle the returnedpostId
value or any thrown errors or Promise rejections.