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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 18:35:58