skip to Main Content

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


  1. 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:

    export const PostClassified = () => {
    
      const postId = useSelector((state) => state.classifieds.postId);
    
      useEffect(() => {
        if (postId) {
          console.log("postID is 2", postId);
          navigate(`/classifieds/${postId}`);
        }
      }, [postId, navigate]);
    
      const handleSubmit = async (e) => {
        e.preventDefault();
        setFormSubmitMessage(false);
    
        newFormData.append("heading", formData.heading);
        newFormData.append("category", formData.category);
        await dispatch(postClassifieds(newFormData));
      };
    
    };

    related : overflow

    Login or Signup to reply.
  2. 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:

    export const postClassifieds = (formData) => async(dispatch) => {
      try {
        const { data } = await axios.post('/posts', formData);
    
        dispatch({
          type: 'POST_SUCCESS',
          payload: data._id,
        });
    
        return data._id; // <-- return postId to UI/calling code
      } catch(error) {
        console.log("error is", error);
    
        dispatch({
          type: 'POST_FAILURE',
          payload: error.message,
        });
    
        throw error; // <-- re-throw error for UI/calling code
      }
    };
    

    Wrap the asynchronous logic in a try/catch to handle the returned postId value or any thrown errors or Promise rejections.

    const handleSubmit = async (e) => {
      e.preventDefault();
    
      try {
        setFormSubmitMessage(false);
        const newFormData = new FormData();
        newFormData.append("heading", formData.heading);
        newFormData.append("category", formData.category);
    
        const postId = await dispatch(postClassifieds(newFormData));
    
        // Success if we get this far 😀
        console.log("postID is 2",postId);
        navigate(`/classifieds/${postId}`);
      } catch(error) {
        // handle/ignore fetch failures/etc
      }
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search