无法在页面刷新时读取未定义反应路由器的属性

Posted

技术标签:

【中文标题】无法在页面刷新时读取未定义反应路由器的属性【英文标题】:Cannot read properties of undefined react router on page refresh 【发布时间】:2021-12-31 07:19:57 【问题描述】:

我正在使用 Edamam 食谱 API 制作食谱应用程序。在我刷新食谱详细信息页面之前,一切正常。当我打开食谱时它工作正常,但是当我刷新页面时,例如 http://localhost:3000/recipe/Pasta%20alla%20Gricia%20Recipe 它给了我一个错误:

Cannot read properties of undefined (reading 'recipe')

我不知道为什么会发生这种情况。当页面没有刷新时它正在工作。

App.js:

import React from "react";
import Home from "./pages/Home";
import ShowRecipe from "./pages/ShowRecipe";
import RecipeDetail from "./pages/RecipeDetail.js";
import 
  Routes,
  Route,
  Link
 from "react-router-dom";

function App()
  return(
    <div>
      <ul>
        <li><Link to="/">Home</Link></li>
        <li><Link to="/recipe">Recipe</Link></li>
      </ul>
      <Routes>
        <Route path="/" element=<Home /> />
        <Route path="/recipe" element=<ShowRecipe /> />
        <Route path="/recipe/:recipeId" element=<RecipeDetail /> />
      </Routes>
    </div>
  )


export default App;

ShowRecipe.js:

import React,useContext,useState,useEffect from "react";
import  Link  from "react-router-dom";
import Context from "../Context"

function ShowRecipe()

  const recipes,getSearch,search,handleChange = useContext(Context)

     let dispRecipe = recipes.map(recipe => (
           <div>
             <img src=recipe.recipe.image />
            <Link to=`/recipe/$recipe.recipe.label`><h1>recipe.recipe.label</h1></Link>
             <p>recipe.recipe.calories</p>
           </div>
         ))

  return(
    <div>
      <form onSubmit=getSearch>
        <input type="text" value=search onChange=handleChange/>
        <button type="submit">Search</button>
      </form>

       dispRecipe

    </div>
  )


export default ShowRecipe;

RecipeDetail.js:

import React,useContext from 'react';
import useParams from "react-router-dom"
import Context from "../Context"



function RecipeDetail()

    const recipes = useContext(Context)
    const recipeId = useParams()

    const currentRecipe = recipes.find(recipe => recipe.recipe.label === recipeId)

    return(
        <div>
            <h1>currentRecipe.recipe.label</h1>
        </div>
    )


export default RecipeDetail

Context.js:

import React, useState, useEffect from "react"

const Context = React.createContext()

function  ContextProvider(children) 
    const API_KEY = "648ac4cc09c9168758cc92c64b18ecfc"
  const API_ID = "854d3ad3"

  const [recipes , setRecipes] = useState([])
  const [search, setSearch] = useState("")
  const [query, setQuery] = useState("pasta")

  let example =`https://api.edamam.com/search?q=$query&from=0&to=20&app_id=$API_ID&app_key=$API_KEY`

  useEffect(()=>
    fetch(example)
      .then(res => res.json())
      .then(data => 
          setRecipes(data.hits);
          console.log(data)
      )
  , [query])

  const handleChange = (e) => 
    setSearch(e.target.value)
    console.log(search)
  

  const getSearch = (e) => 
    e.preventDefault()
    setQuery(search)
  

  console.log(recipes)

  return (
        <Context.Provider value=
            recipes,
            search,
            getSearch,
            handleChange,
        >
            children
        </Context.Provider>
    )



export ContextProvider, Context

【问题讨论】:

我认为您需要检查currentRecipe 是否在RecipeDetail 中定义。当应用程序最初启动时,currentRecipe 可能是 undefinedrecipes 仍然是一个空数组 [])。类似currentRecipe ? &lt;h1&gt;currentRecipe.recipe.label&lt;/h1&gt; : &lt;h1&gt;Recipe not found&lt;/h1&gt; @Jackyef 它现在正在工作,首先它说没有找到食谱,但 1 秒后它会显示食谱。你能告诉这是怎么回事吗?这是让它工作的唯一方法吗? 【参考方案1】:

在这一行中,您正在为您的 React 应用程序创建上下文

const Context = React.createContext()

createContext 调用的参数是初始状态——这里它是未定义的。因为没有初始状态,所以这个上下文的值是未定义的。

这不是很直观,但这个初始值是您应用程序中的组件在第一次渲染期间将收到的值——即使您在提供程序中设置了value

console.log(useContext(Context)) // undefined

您稍后尝试解构这个未定义的对象

const recipes,getSearch,search,handleChange = useContext(Context)

这会导致错误“无法读取未定义的属性”。

当您使用 react 的路由移动到页面时,上下文已经有足够的时间使用您期望的值进行初始化。当您刷新页面时,上下文没有足够的时间使用您期望的值进行初始化。因此,您最终会看到此错误。

正如一位助手已经说过的,您应该在尝试访问之前检查上下文中的值是否可用。您有多种选择。

等待定义上下文值

const context = useContext(Context)

if (!context) 
  return "Loading..."

在上下文中设置初始值

const Context = React.createContext(
  recipes: []
)

在您的上下文中建模加载状态

const Context = React.createContext(
  recipes: null,
  isLoading: true,
)

// ...

const [isLoadingRecipes , setLoadingRecipes] = useState(true)

// ...

  useEffect(()=>
    setLoadingRecipes(true)
    fetch(example)
      .then(res => res.json())
      .then(data => 
          setRecipes(data.hits);
          setLoadingRecipes(false);
          console.log(data)
      )
  , [query])

// ...

const recipes, isLoading = useContext(Context)

if (!recipes && isLoading) 
  return "Loading...";


【讨论】:

以上是关于无法在页面刷新时读取未定义反应路由器的属性的主要内容,如果未能解决你的问题,请参考以下文章

反应路由抛出错误:无法读取浏览器路由器未定义的属性“推送”

使用反应路由器 V6 和刷新页面时我得到空白页面

Apollo 查询在页面刷新时运行,但不使用反应路由器导航?

JS 编译时 React DOM 更新。但是刷新浏览器会给出错误'无法读取未定义的属性(读取'1')'

刷新页面后Vuex无法读取属性

在 React 路由器中使用“链接到”时如何刷新页面?