使用Link组件传递状态至其他页面时遇到错误
问题:无法通过location.state传递食谱数据到RecipePage组件
开发功能时,从API获取食谱信息后将数组映射为可点击列表项,点击后跳转至对应RecipePage页面,但在RecipePage中出现错误:
Uncaught TypeError: Cannot destructure property 'recipe' of 'location.state' as it is null
尝试过使用navigate()和URL参数传递状态,均未解决问题。相关代码如下:
SearchBar.jsx
import React, { useState } from "react"; import {FaSearch} from "react-icons/fa"; import { Link } from "react-router-dom"; import "./SearchBar.css"; function SearchBar() { const [input, setInput] = useState(""); const [searchList, setSearchList] = useState(); const [listLoaded, setListLoaded] = useState(false); const options = { method: 'GET', headers: { } }; const handleChange = (value) => { setInput(value); } const keyHandler = (e) => { if(e.code === "Enter") { fetchData(input); } } const fetchData = (value) => { let queryString = new URLSearchParams(value).toString(); const url = `https://food-recipes-with-images.p.rapidapi.com/?q=${queryString}`; fetch(url, options) .then(response => response.json()) .then((data) => { setSearchList(data['d']); if (!data['d']) { console.log("The request failed"); } else { setListLoaded(true); console.log(data['d']); } }) } return( <div> <div className="input-wrapper"> <FaSearch id="search-icon" /> <input placeholder="Search" value={input} onChange={(e) => handleChange(e.target.value)} onKeyPress={(e) => keyHandler(e)} /> </div> <div className="search-result-body"> {listLoaded && searchList.map((item) => <Link key={item.id} to={{ pathname: `/recipe-item/${encodeURIComponent(item.Title)}`, state: { recipe: item } }} className="recipe-link"> <div className="search-result-item"> <img src={item.Image} /> <p>{item.Title}</p> </div> </Link> )} </div> </div> ) } export default SearchBar;
App.jsx
import { useState } from 'react'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import Navbar from './components/Navbar/Navbar.jsx'; import Dashboard from "./components/Dashboard/Dashboard.jsx"; import Inventory from './components/Inventory/Inventory.jsx'; import MealPlanner from './components/MealPlanner/MealPlanner.jsx'; import Profiles from './components/Profiles/Profiles.jsx'; import Recipes from './components/Recipes/Recipes.jsx'; import RecipePage from './pages/RecipePage/RecipePage.jsx'; import './App.css' function App() { return ( <div> <BrowserRouter> <Navbar /> <Routes> <Route path="/" element={<Dashboard />} /> <Route path="/inventory" element={<Inventory />} /> <Route path="/mealplanner" element={<MealPlanner />} /> <Route path="/profiles" element={<Profiles/>} /> <Route path="/recipes" element={<Recipes />} /> <Route path="/recipe-item/:title" element={<RecipePage />} /> </Routes> </BrowserRouter> </div> ) } export default App
RecipePage.jsx
import React from "react"; import { useLocation } from "react-router-dom"; function RecipePage() { const location = useLocation(); const { recipe } = location.state || {}; console.log(recipe); if (!recipe) { return ( <div> <p>No recipe data found!</p> </div> ); } return ( <div> <h2>{recipe.Title}</h2> </div> ); } export default RecipePage;
解决方案
1. 安全解构location.state,避免null报错
当前代码的const { recipe } = location.state || {}存在逻辑漏洞,||会在location.state为0或空字符串等假值时也返回空对象,改用空值合并运算符??更精准,仅在location.state为null或undefined时生效:
const { recipe } = location.state ?? {};
或者直接使用可选链简化:
const recipe = location.state?.recipe;
2. 处理直接访问路由的场景
如果用户直接在地址栏输入/recipe-item/xxx,location.state必然为null。此时可通过URL参数重新请求API获取数据,或引导用户从搜索列表进入:
修改RecipePage.jsx:
import React, { useEffect, useState } from "react"; import { useLocation, useParams } from "react-router-dom"; function RecipePage() { const location = useLocation(); const { title } = useParams(); const [recipe, setRecipe] = useState(location.state?.recipe); const [loading, setLoading] = useState(!recipe); const [error, setError] = useState(false); // 无state数据时,通过标题重新请求API useEffect(() => { if (!recipe && title) { setLoading(true); const decodedTitle = decodeURIComponent(title); const queryString = new URLSearchParams(decodedTitle).toString(); const url = `https://food-recipes-with-images.p.rapidapi.com/?q=${queryString}`; const options = { method: 'GET', headers: { // 补充你的API密钥等必填请求头 } }; fetch(url, options) .then(response => response.json()) .then((data) => { if (data['d']?.length > 0) { // 取返回结果中的第一个匹配食谱 setRecipe(data['d'][0]); } else { setError(true); } setLoading(false); }) .catch(() => { setError(true); setLoading(false); }); } }, [recipe, title]); if (loading) return <div>加载中...</div>; if (error || !recipe) return <div>未找到食谱数据,请从搜索列表进入</div>; return ( <div> <h2>{recipe.Title}</h2> {/* 渲染其他食谱内容 */} </div> ); } export default RecipePage;
3. 改用ID作为URL参数(更可靠)
用食谱ID代替标题作为URL参数,可避免标题重复、编码等问题,也更便于通过ID请求单个食谱数据(若API支持):
- 修改SearchBar.jsx中的Link:
<Link key={item.id} to={{ pathname: `/recipe-item/${item.id}`, state: { recipe: item } }} className="recipe-link">
- 修改App.jsx中的路由:
<Route path="/recipe-item/:id" element={<RecipePage />} />
- 在RecipePage中通过ID请求数据,确保刷新或直接访问也能获取到完整信息。
内容的提问来源于stack exchange,提问作者MWBradshaw
相关产品推荐
相关产品推荐

