You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中handleSubmit内useSelector获取的postId仍为null的问题

问题:React Redux提交表单后无法获取最新postId进行路由跳转

提交表单后触发action调用API,API返回postId并存储到reducer中。组件通过useSelector获取postId的最新状态,但在handleSubmit函数内打印postId显示为null,函数外部却能正常获取到该值,无法成功跳转到/classifieds/{postId}路由。

相关代码

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) // 提交后能正确显示ID
  
  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) // 此处显示null
    navigate(`/classifieds/${postId}`)
  };

  return (
    <div className="section properties">
      <div className="container">
        {classifiedsLoading
          ? (<></>)
          : <Form onSubmit={handleSubmit} encType="multipart/form-data" className="form-post">
            {/* 表单内容 */}
          </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";

// 发布分类信息
export const postClassifieds = (formData) => async(dispatch) => {
  try {
    const response = await axios.post('/posts', formData);
    dispatch({
      type: 'POST_SUCCESS',
      payload: response.data._id, // 假设服务器返回新文档的ID
    });
    return response.data._id; // 返回postId供组件直接使用
  } catch(error) {
    console.log("error is", error);
    dispatch({
      type: 'POST_FAILURE',
      payload: error.message,
    });
    throw error; // 抛出错误方便组件处理
  }
};

// 设置加载状态
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, // 修正拼写:统一使用postId(之前是postID大写D)
  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;
  }
};

问题原因与解决方案

问题根源

handleSubmit中虽然await dispatch(postClassifieds(newFormData))等待了API请求完成,但Redux状态更新是异步触发组件重新渲染的过程。当前函数执行时,组件还没完成重新渲染,所以postId仍然是更新前的旧值(null)。而函数外部的console.log是在组件每次渲染时执行,所以能拿到更新后的最新值。

解决方案1:直接从action返回值获取postId

修改postClassifieds action,在请求成功后返回response.data._id,这样组件可以直接通过await dispatch(...)拿到最新的postId,无需等待组件重新渲染:

const handleSubmit = async (e) => {
  e.preventDefault();
  setFormSubmitMessage(false);
    
  const newFormData = new FormData(); // 移到函数内部避免重复使用同一个实例
  newFormData.append("heading",formData.heading);
  newFormData.append("category",formData.category);
  
  try {
    const postId = await dispatch(postClassifieds(newFormData));
    console.log("postID is 2",postId); // 此处能拿到最新ID
    navigate(`/classifieds/${postId}`);
  } catch (error) {
    // 处理提交失败逻辑
    setFormSubmitMessage(true);
  }
};

解决方案2:用useEffect监听postId变化跳转

如果不想修改action,可以通过useEffect监听postId的变化,当它从null变为有效值时执行路由跳转:

useEffect(() => {
  if (postId) {
    navigate(`/classifieds/${postId}`);
    // 可选:跳转后重置postId,避免重复跳转
    // dispatch(resetPostId()); // 需要新增对应的action和reducer逻辑
  }
}, [postId, navigate]);

const handleSubmit = async (e) => {
  e.preventDefault();
  setFormSubmitMessage(false);
    
  const newFormData = new FormData();
  newFormData.append("heading",formData.heading);
  newFormData.append("category",formData.category);
  
  await dispatch(postClassifieds(newFormData));
};

额外注意点

  • 修正reducer中初始状态的拼写错误:原代码初始状态是postID(大写D),但更新时赋值的是postId(小写d),这会导致状态不一致,统一改为postId。
  • newFormData建议移到handleSubmit函数内部,避免多次调用时复用同一个FormData实例导致数据混乱。

内容的提问来源于stack exchange,提问作者kheman garg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 20:03:18