React Router navigate传递setState函数报错,求解决方案
解决React Router跳转时传递setState函数导致的克隆报错问题
报错BoundFunctionObject object could not be cloned的根本原因是:React Router的location.state要求传递的数据必须是可序列化的(符合结构化克隆算法要求),而React的setProducts这类setState函数是绑定了组件实例的函数,无法被序列化,传递时就会触发克隆失败的错误。
以下是几种可行的解决方案:
方案1:返回时重新拉取数据(推荐)
放弃通过路由传递状态更新函数,转而在Products组件中监听路由变化,当从NewProduct页面返回时,重新请求最新的产品列表。这种方式既规避了序列化问题,也能保证数据和后端完全一致。
修改Products组件:
import { useNavigate, useLocation, useEffect } from 'react-router-dom'; function Products() { const navigate = useNavigate(); const location = useLocation(); const [products, setProducts] = useState({}); // 封装获取产品列表的函数 const fetchProducts = async () => { const res = await ctx.axios.get("/products"); setProducts(res.data); }; // 组件挂载时初始化数据 useEffect(() => { fetchProducts(); }, []); // 监听路由变化,从新增页面返回时重新拉取数据 useEffect(() => { if (location.state?.fromNewProduct) { fetchProducts(); // 清除state,避免重复触发 navigate(location.pathname, { replace: true, state: {} }); } }, [location, navigate]); return ( // ... 原有代码 <button onClick={() => navigate('/admin/products/new', { state: { category: selectedCategory } })} className=" bg-green-500 text-white px-4 py-2 rounded my-[1rem]" > 新增产品 </button> // ... 原有代码 ); }
修改NewProduct组件的onSave函数:
async function onSave() { // ... 原有表单提交逻辑 const res = await ctx.axios.post("/products", formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); // 返回时标记来源,触发Products组件更新 navigate('/admin/products', { state: { fromNewProduct: true } }); }
方案2:使用React Context共享状态更新函数
把setProducts放到全局Context中,让NewProduct组件直接从Context获取,无需通过路由传递。
- 创建ProductContext文件:
// ProductContext.js import { createContext, useContext, useState } from 'react'; const ProductContext = createContext(); export function ProductProvider({ children }) { const [products, setProducts] = useState({}); return ( <ProductContext.Provider value={{ products, setProducts }}> {children} </ProductContext.Provider> ); } export function useProductContext() { return useContext(ProductContext); }
- 在根组件中包裹Provider:
// App.js import { ProductProvider } from './ProductContext'; function App() { return ( <ProductProvider> {/* 路由及其他组件 */} </ProductProvider> ); }
- 修改Products组件:
import { useNavigate } from 'react-router-dom'; import { useProductContext } from './ProductContext'; function Products() { const navigate = useNavigate(); const { products } = useProductContext(); return ( // ... 原有代码 <button onClick={() => navigate('/admin/products/new', { state: { category: selectedCategory } })} className=" bg-green-500 text-white px-4 py-2 rounded my-[1rem]" > 新增产品 </button> // ... 原有代码 ); }
- 修改NewProduct组件:
import { useLocation, useNavigate } from 'react-router-dom'; import { useProductContext } from './ProductContext'; function NewProduct() { const location = useLocation(); const navigate = useNavigate(); const { category } = location?.state; const { setProducts } = useProductContext(); async function onSave() { // ... 原有表单提交逻辑 const res = await ctx.axios.post("/products", formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); const p = res.data; // 注意:状态更新必须保持不可变性,不能直接push原数组 setProducts(prev => ({ ...prev, [category]: [...(prev[category] || []), { hash: p.uuid, imgFilename: p.imgFilename, title: p.title, description: p.description, price: p.price }] })); navigate('/admin/products') } // ... 原有代码 }
方案3:使用全局状态管理库(如Redux)
如果项目已使用Redux这类全局状态管理工具,可将products状态放到全局store中,NewProduct组件通过dispatch更新状态,Products组件订阅状态即可。这种方式适合多组件共享状态的场景。
内容的提问来源于stack exchange,提问作者milanHrabos
相关产品推荐
相关产品推荐

