【问题标题】:Can't render component on State Change (TypeError: Cannot read property 'map' of undefined)无法在状态更改时呈现组件(TypeError:无法读取未定义的属性“地图”)
【发布时间】:2021-09-14 23:53:57
【问题描述】:

我是新来的,正在处理一个从外部 api 获取配方详细信息的小项目。 一旦页面加载,我就能够呈现食谱列表,但我面临的挑战是,一旦状态中的值发生变化,我正在尝试进行条件渲染以呈现食谱详细信息。即:如果 PageIndex === 1 则加载配方列表,如果 PageIndex === 0 则加载配方详细信息。

配方列表加载没有任何问题,我可以转到配方详细信息页面,但是一旦我单击转到配方详细信息,配方列表组件就会卸载。当我单击一个按钮返回到配方列表时,我得到一个错误 TypeError: Cannot read property 'map' of undefined.

当我在按钮单击时将索引值更改回 1 时,如何让组件重新呈现。

下面是我的代码

注意:我将 props handleIndex 传递给 RecipeDetails,这是我在用户单击返回食谱列表时运行的代码。

应用js

import React, { Fragment, useState, useEffect } from 'react';
import { recipes } from './tempList';
import RecipeList from './components/RecipeList';
import RecipeDetails from './components/RecipeDetails';
import './App.css';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import Header from './components/Header';

const App = () => {
  const [data, setData] = useState({
    recipes: [],
    url: 'https://forkify-api.herokuapp.com/api/search?q=pizza',
    details_id: 47746,
    pageIndex: 1,
  });

  const { url, recipes, details_id, pageIndex } = data;

  const getRecipes = () => {
    fetch(url)
      .then((res) => res.json())
      .then((jsonData) => {
        setData({ ...data, recipes: jsonData.recipes });
      })
      .catch((err) => {
        console.log(err);
      });
  };

  useEffect(() => {
    getRecipes();
  }, []);

  const handleIndex = (index) => {
    setData({ pageIndex: index });
  };

  const handleDetails = (index, id) => {
    setData({
      pageIndex: index,
      details_id: id,
    });
  };

  return (
    <Fragment>
      <Header />
      {pageIndex === 1 ? (
        <RecipeList recipes={recipes} handleDetails={handleDetails} />
      ) : (
        <RecipeDetails details_id={details_id} handleIndex={handleIndex} />
      )}
    </Fragment>
  );
};

配方详情

import React, { Fragment, useState, useEffect } from 'react';
import { Link, BrowserRouter } from 'react-router-dom';

const RecipeDetails = ({ handleIndex, details_id }) => {
  const [recipeDetails, setRecipeDetails] = useState({
    recipe: [],
  });
  const { recipe } = recipeDetails;

  const id = details_id;
  const url = `https://forkify-api.herokuapp.com/api/get?rId=${id}`;

  const getRecipe = () => {
    fetch(url)
      .then((res) => res.json())
      .then((data) => {
        setRecipeDetails({
          recipe: data.recipe,
        });
      })
      .catch((err) => {
        console.log(err);
      });
  };

  useEffect(() => {
    getRecipe();
  }, []);

  console.log(recipe);

  const {
    image_url,
    title,
    source_url,
    publisher,
    publisher_url,
    ingredients,
  } = recipe;

  return (
    <Fragment>
      <div className='container'>
        <div className='row'>
          <div className='col-10 mx-auto col-md-6 my-3'>
            <BrowserRouter>
              <Link to='/'>
                <button
                  type='button'
                  className='btn btn-warning mb-5 text-capitalize'
                  onClick={() => handleIndex(1)}
                >
                  back to recipe list
                </button>
              </Link>
            </BrowserRouter>
            <img src={image_url} alt='recipe' className='d-block w-100' />
          </div>
          <div className='col-10 mx-auto col-md-6 my-3'>
            <h6 className='text-uppercase'>{title}</h6>
            <h6 className='text-warning text-capitalize'>
              Publisher: {publisher}
            </h6>
            <a
              href={publisher_url}
              target='_blank'
              rel='noopener noreferrer'
              className='btn btn-primary mt-2 text-capitalize'
            >
              publisher page
            </a>
            <a
              href={source_url}
              target='_blank'
              rel='noopener noreferrer'
              className='btn btn-success mx-3 mt-2 text-capitalize'
            >
              recipe url
            </a>
            <ul className='list-group mt-4'>
              <h2 className='mt-3 mb-4'>Ingredients</h2>
              {ingredients === undefined ? (
                <p>Loading...</p>
              ) : (
                ingredients.map((item, index) => {
                  return (
                    <li key={index} className='list-group-item text-slanted'>
                      {item}
                    </li>
                  );
                })
              )}
            </ul>
          </div>
        </div>
      </div>
    </Fragment>
  );
};

export default RecipeDetails;

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    试试这个功能

      const handleIndex = (index) => {
        setData(state => ({ ...state, pageIndex: index }));
      };
    

    【讨论】:

    • 我尝试了该功能并得到了同样的错误。
    【解决方案2】:

    您需要更新 RecipeDetails.js 中的某些代码行 替换

    const { recipe } = recipeDetails;
    

    作为

    const { recipe = {} } = recipeDetails;
    

    那么,

    替换

    const {
    image_url, 
    title, 
    source_url, 
    publisher, 
    publisher_url, 
    ingredients 
    } = recipe;
    

    作为

     const {
     image_url = '',
     title = '',
     source_url = '',
     publisher = '',
     publisher_url = '',
     ingredients = [],
     } = recipe;
    

    只需对变量进行初始化,否则会引发错误。这应该可以解决问题。

    还要添加一件事,handleIndex 函数中应该始终将先前的状态作为副本,然后在您的状态变量中更新。

    我更新了以下功能:

     const getRecipes = () => {
     fetch(`${url}${string}`)
      .then(res => res.json())
      .then(jsonData => {
        setData({ ...data, recipes: jsonData.recipes });
      })
      .catch(err => {
        console.log(err);
      });
      };
    

    const handleIndex = index => {
        setData({ ...data, pageIndex: index });
    };
    

    const handleDetails = (index, id) => {
         setData({
             ...data,
             pageIndex: index,
             details_id: id
         });
    };
    

    const handleChange = e => {
        const value = e.target.value;
        setData({ ...data, search: value });
    };
    

    const handleSubmit = e => {
       e.preventDefault();
       setData({ ...data, url: `${base_url}${search}`, search: '' });
    };
    

    【讨论】:

    • 感谢您的意见。按照你的代码,但仍然得到同样的错误。我真的不知道我做错了什么..你能看看我是如何渲染组件的吗?你认为我在这里使用 useEffect 的方式有误吗?
    • 能否请您通过分叉项目添加您的RecipeList.js 并共享相同stackblitz.com/edit/react-6q4njr?file=src%2FApp.js 的url,也许我们可以解决它,因为当我只是运行RecipeDetails 时它工作正常。 js 组件。当它点击其中一个项目列表后进入recipeDetails页面时,问题一定是存在的。
    • 您好,这里是分叉项目的链接。 stackblitz.com/edit/react-gfdgx4?file=src/App.js
    • 嘿,我已经在您的代码中进行了更正,无论您在哪里设置 setData 函数的数据,属性都不会持续存在,因为它没有复制旧对象,因此直接更新是让您的数据只要坚持你试图更新的属性,休息就消失了。这是链接stackblitz.com/edit/react-ad8hh7?file=src%2FApp.js。希望对您有所帮助!
    • 嘿!我需要最后的帮助。当用户搜索食谱并且结果不可用时,我试图捕获并返回错误,但我遇到了一些错误。这是带有我更新代码的分叉项目。我会很感激你的帮助谢谢。 stackblitz.com/edit/react-mhbuur?file=src%2FRecipeList.js
    猜你喜欢
    • 2019-07-25
    • 2020-06-17
    • 2021-11-18
    • 1970-01-01
    • 2019-03-02
    • 1970-01-01
    相关资源
    最近更新 更多