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
相关产品推荐
相关产品推荐

