React中通过Link传递多状态时为何获取到null?
问题:React Router Link传递包含函数的对象时State为null
问题现象
使用react-router-dom的Link组件传递包含item数据和handleCartClick函数的对象myData作为state到子组件ProductDetails时,接收方的location.state为null;但仅传递单个item数据时功能完全正常。
相关验证场景:
- 单状态传递的代码:仅传递
item作为state的Link组件代码 - 单状态传递的输出:
location.state正常包含item数据的控制台输出 - 多状态传递的代码:传递包含
item和handleCartClick的对象作为state的Link组件代码 - 多状态传递的输出:
location.state显示为null的控制台输出
原因分析
React Router在通过Link的state属性传递数据时,会对数据进行序列化处理(类似JSON.stringify逻辑)。而函数无法被JSON序列化,当你在myData中包含handleCartClick函数时,整个state的序列化过程失败,最终导致传递的state变为null。
解决方案
方案1:避免通过路由State传递函数
函数属于逻辑范畴,不适合通过路由state传递。建议只传递item数据,在子组件中通过全局状态管理(如React Context、Redux)或组件上下文获取handleCartClick函数。
修改父组件代码(仅传递item)
// TopCards 父组件 import React from 'react' import { Link } from 'react-router-dom'; import "../HomePage/TopPicks.css"; const TopCards = ({item,id,handleCartClick}) => { const {title,image,price,desc,rating} = item const toIndian = (number)=>{ let x=number.toString(); let lastThree = x.substring(x.length-3); let otherNumbers = x.substring(0,x.length-3); if(otherNumbers != '') lastThree = ',' + lastThree; let res = otherNumbers.replace(/\B(?=(\d{2})+(?!\d))/g, ",") + lastThree; return res; } // 仅传递item数据 const myData = { item: item, }; return ( <div className="col-sm-3 mb-3 mb-sm-0 topPicks_cards "> <Link to='product-details' state={myData}> <div className="card topPicks_card" style={{width: "286px"}}> <img src= {image} className="card-img-top" alt="..." /> <div className="card-body"> <h6 className="card-title">{title}</h6> <p className="card-text">{`Rs. ${toIndian(price*80)}`}</p> <div className="rating">{rating.rate}</div> </div> </div> </Link> </div> ) } export default TopCards
通过React Context共享handleCartClick
- 创建CartContext文件:
// CartContext.js import { createContext, useContext } from 'react'; const CartContext = createContext(); export const CartProvider = ({ children, handleCartClick }) => { return ( <CartContext.Provider value={{ handleCartClick }}> {children} </CartContext.Provider> ); }; export const useCart = () => useContext(CartContext);
- 在根组件包裹CartProvider:
// App.js import { CartProvider } from './CartContext'; import { handleCartClick } from './your-path'; // 导入你的handleCartClick函数 import { BrowserRouter as Router } from 'react-router-dom'; function App() { return ( <Router> <CartProvider handleCartClick={handleCartClick}> {/* 路由配置和其他组件 */} </CartProvider> </Router> ); }
- 在子组件中使用Context:
// ProductDetails子组件 import React,{useState} from 'react' import { useLocation } from 'react-router-dom' import { useCart } from '../CartContext'; import Navbar from '../HomePage/Navbar' import '../ProductDetails/productDetails.css' import Footer from "../HomePage/Footer" const ProductDetails = () => { const location = useLocation() const { handleCartClick } = useCart(); const state = location.state; // 增加空值检查,避免直接访问路由时报错 if (!state?.item) { return <div>无效的产品链接</div>; } const [count, setCount] = useState(0); const handleDecrement = ()=>{ setCount(preValue => preValue < 1 ? 0 : preValue - 1) } const handleIncrement = ()=>{ setCount(preValue => preValue + 1) } const toIndian = (number)=>{ let x=number.toString(); let lastThree = x.substring(x.length-3); let otherNumbers = x.substring(0,x.length-3); if(otherNumbers !== '') lastThree = ',' + lastThree; let res = otherNumbers.replace(/\B(?=(\d{2})+(?!\d))/g, ",") + lastThree; return res; } // 调用共享的handleCartClick const handleAddToCart = () => { handleCartClick(state.item); } return ( <div className='product-details'> <Navbar/> <div className="container"> <div className="product-detail"> <div className="product-image"> <img src={state.item.image} alt={state.item.title} /> </div> <div className="product-info"> <div className="product-title"> <h3>{state.item.title}</h3> </div> <div className="desciption">{state.item.description}</div> <div className="rating">{state.item.rating.rate}</div> <div className="product-price"> <h3>Rs. {toIndian(state.item.price*80)}</h3> </div> <div className="counter"> <span> <button type="button" className="btn btn-light" onClick={handleDecrement}>-</button> </span> <span> <button type="button" className="btn btn-light">{count}</button> </span> <span> <button type="button" className="btn btn-light" onClick={handleIncrement}>+</button> </span> <div className="buying"> <div className="buy"> <button type="button" className="btn btn-primary btn-lg">Buy Now</button> </div> <div className="addtocart"> <button type="button" className="btn btn-primary btn-lg" onClick={handleAddToCart}>Add to Cart</button> </div> </div> </div> </div> </div> </div> <Footer></Footer> </div> ) } export default ProductDetails
额外优化:强制空值检查
无论采用哪种方案,都必须在子组件中对location.state进行空值检查,避免用户直接输入路由地址时出现报错。
内容的提问来源于stack exchange,提问作者Alok Singh
相关产品推荐
相关产品推荐

