如何结合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
相关产品推荐
相关产品推荐

