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;
}
};
The issue here is that the submit handler has a stale closure over the selected
postIdstate.Update the code such that your
postClassifiedsaction returns a resolved value to the calling code that can be awaited.Example:
Wrap the asynchronous logic in a
try/catchto handle the returnedpostIdvalue or any thrown errors or Promise rejections.