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

如何结合React Router与Redux调度函数?表单提交dispatch问题

解决React Router路由Action中无法使用useDispatch的问题

问题核心

路由配置中的saveEvent是路由Action函数,并非React组件或自定义钩子,因此不能直接调用useDispatch这类React Hooks(Hooks只能在React组件/自定义钩子的顶层作用域使用)。

可行解决方案

方案1:直接使用Redux Store实例

如果你的Redux Store在创建时导出了实例(常规配置方式),可以直接在Action函数中导入Store并调用dispatch。

假设你的Store配置文件(store/store.js)如下:

import { configureStore } from '@reduxjs/toolkit';
import modalReducer from './modalSlice';
import tasksReducer from './tasksSlice';

// 导出store实例
export const store = configureStore({
  reducer: {
    modal: modalReducer,
    tasks: tasksReducer,
  },
});

export { hide, show, add } from './tasksSlice';

修改saveEvent函数:

import { store } from '../store/store';
import { add } from '../store/store';
import { redirect } from 'react-router-dom';

const saveEvent = async ({ request }) => {
  const data = await request.formData();

  const dataForm = {
    title: data.get('title'),
    desc: data.get('desc'),
  };

  // 直接通过store实例dispatch action
  store.dispatch(add(dataForm));

  // 提交完成后返回重定向(React Router特性)
  return redirect('/tasks');
};

方案2:在组件内部处理表单提交

放弃使用React Router的路由Action,直接在NewTask组件内通过表单的onSubmit事件处理提交逻辑,这样就能正常使用useDispatch。

修改NewTask组件:

import React from "react";
import { useNavigate } from 'react-router-dom'
import Modal from "../components/Modal"
import Button from '../components/Button'
import { useSelector, useDispatch } from 'react-redux'
import { hide, show, add } from '../store/store'

const NewTask = () => {
  const dispatch = useDispatch()
  const navigate = useNavigate()
  const status = useSelector(state => state.modal.show)
  const closeModal = () => dispatch(hide())
  const openModal = () => dispatch(show())

  // 自定义提交处理函数
  const handleSubmit = async (e) => {
    e.preventDefault()
    const formData = new FormData(e.target)
    const dataForm = {
      title: formData.get('title'),
      desc: formData.get('desc'),
    }

    dispatch(add(dataForm))
    e.target.reset() // 重置表单
    closeModal() // 关闭模态框
    navigate('/tasks') // 跳转到任务列表页
  }

  return <>
    {status && <Modal hide={closeModal}>
      <Button onClick={closeModal} className="btn btn-success"/>
    </Modal>}
    {/* 使用原生form标签,绑定自定义onSubmit */}
    <form onSubmit={handleSubmit} id="form">
      <div className="mb-3">
        <label htmlFor="title" className="form-label">Title</label>
        <input type="text" name="title" id="title" className="form-control" />
      </div>
      <div className="mb-3">
        <label htmlFor="desc" className="form-label">Description</label>
        <textarea cols="30" rows="10" name="desc" id="desc" className="form-control"></textarea>
      </div>
      <div className="mb-3">
        <button type="reset" className="btn btn-warning">Cancel</button>
        <button type="submit" className="btn btn-success">Save</button>
      </div>
    </form>
  </>
}

export default NewTask

同时需要移除路由配置中的action: myaction(原路由中myaction应为saveEvent的笔误)。

方案3:结合Redux异步Thunk处理复杂逻辑

如果提交过程需要异步操作(比如调用后端API),可以用Redux Toolkit的createAsyncThunk封装异步逻辑,再在路由Action中调用。

示例(tasksSlice.js):

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';

// 定义异步Thunk
export const addTaskAsync = createAsyncThunk(
  'tasks/addTaskAsync',
  async (taskData) => {
    // 模拟API请求
    const response = await fetch('/api/tasks', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(taskData),
    });
    return response.json();
  }
);

// 在Slice中处理Thunk的状态
const tasksSlice = createSlice({
  name: 'tasks',
  initialState: [],
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(addTaskAsync.fulfilled, (state, action) => {
        state.push(action.payload);
      });
  },
});

export default tasksSlice.reducer;

修改saveEvent:

import { store } from '../store/store';
import { addTaskAsync } from '../store/slices/tasksSlice';
import { redirect } from 'react-router-dom';

const saveEvent = async ({ request }) => {
  const data = await request.formData();
  const dataForm = {
    title: data.get('title'),
    desc: data.get('desc'),
  };

  // 调用异步Thunk并等待完成
  await store.dispatch(addTaskAsync(dataForm)).unwrap();

  return redirect('/tasks');
};

内容的提问来源于stack exchange,提问作者Leonardo Marcatti

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 04:36:15