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

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获取,无需通过路由传递。

  1. 创建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);
}
  1. 在根组件中包裹Provider:
// App.js
import { ProductProvider } from './ProductContext';

function App() {
  return (
    <ProductProvider>
      {/* 路由及其他组件 */}
    </ProductProvider>
  );
}
  1. 修改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>
    // ... 原有代码
  ); 
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 17:53:18