【问题标题】:Restapi GET call making website extremely slowRest Api GET 调用网站极慢
【发布时间】:2021-05-19 12:34:03
【问题描述】:

我正在使用带有 typescript 和自制 RESTapi 的 React。我在一个页面上发出 GET 请求,由于某种原因,我的网站非常慢,有时甚至完全阻塞了 UI - 这在 getData() 和 renderMultipleCards() 函数中实现 GET RESTapi 调用后开始发生。

有谁知道为什么会这样,以及如何解决?

import "../css/stylesheet.css";
import Image from 'react-bootstrap/Image'
import Omnivores from "../images/ProductBanners/omnivoresCrop.png";
import Carnivores from "../images/ProductBanners/CarnivoreCrop.png";
import Herbivores from "../images/ProductBanners/herbivoreCrop.png";
import Small from "../images/ProductBanners/smallDinosaursCrop.png";
import Medium from "../images/ProductBanners/mediumDinosaursCrop.jpg";
import Large from "../images/ProductBanners/largeDinosCrop.png";
import React, { useEffect, useState } from "react";
import { Button, Card, CardDeck } from "react-bootstrap";
const ALL_DINOS = "http://localhost:3000/velocishop/products";
const SMALL_DINOS = "http://localhost:3000/velocishop/size/Small/products";
const MEDIUM_DINOS = "http://localhost:3000/velocishop/size/Medium/products";
const LARGE_DINOS = "http://localhost:3000/velocishop/size/Large/products";
const CARNIVORE_DINOS = "http://localhost:3000/velocishop/diet/Carnivore/products";
const HERBIVORE_DINOS = "http://localhost:3000/velocishop/diet/Herbivore/products";
const OMNIVORE_DINOS = "http://localhost:3000/velocishop/diet/Omnivore/products";
const ALL_DINOS_API_CALL = "http://localhost:3005/velocishop/products";
const SMALL_DINOS_API_CALL = "http://localhost:3005/velocishop/size/Small/products";
const MEDIUM_DINOS_API_CALL = "http://localhost:3005/velocishop/size/Medium/products";
const LARGE_DINOS_API_CALL = "http://localhost:3005/velocishop/size/Large/products";
const CARNIVORE_DINOS_API_CALL = "http://localhost:3005/velocishop/diet/Carnivore/products";
const HERBIVORE_DINOS_API_CALL = "http://localhost:3005/velocishop/diet/Herbivore/products";
const OMNIVORE_DINOS_API_CALL = "http://localhost:3005/velocishop/diet/Omnivore/products";



export default function AllProducts() {
  var header: string = "";
  var url: string = "";
  var apiUrl: string = "";
  const [dinos, setDinos] = useState<Array<any>>([]);

  const handleSetDinos = (e: any) => {
    setDinos(e);
    console.log(dinos)
  };

  const renderImageAndHeader = () => {
    url = window.location.href
    switch (url) {
      case ALL_DINOS: {
        header = "All Dinosaurs"
        apiUrl = ALL_DINOS_API_CALL
        console.log(apiUrl)
        return Omnivores
      }
      case SMALL_DINOS: {
        header = "Small Dinosaurs"
        apiUrl = SMALL_DINOS_API_CALL
        console.log(url)
        return Small
      }
      case MEDIUM_DINOS: {
        header = "Medium Dinosaurs"
        apiUrl = MEDIUM_DINOS_API_CALL
        console.log(url)
        return Medium
      }
      case LARGE_DINOS: {
        header = "Large Dinosaurs"
        apiUrl = LARGE_DINOS_API_CALL
        console.log(url)
        return Large
      }
      case CARNIVORE_DINOS: {
        header = "Carnivore Dinosaurs"
        apiUrl = CARNIVORE_DINOS_API_CALL
        console.log(url)
        return Carnivores
      }
      case HERBIVORE_DINOS: {
        header = "Herbivore Dinosaurs"
        apiUrl = HERBIVORE_DINOS_API_CALL
        console.log(url)
        return Herbivores
      }
      case OMNIVORE_DINOS: {
        header = "Omnivore Dinosaurs"
        apiUrl = OMNIVORE_DINOS_API_CALL
        console.log(url)
        return Omnivores
      }
    }
  }

  function getData(url: string) {
    fetch(url, {
      method: "GET",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
    })
      .then(response => response.json())
      .then((data) => {
        handleSetDinos(data)
      })
      .catch(err => console.log(err))
  }


  function renderMultipleCards() {
    getData(apiUrl)
    return (
      <CardDeck>
        {dinos.map((dino) => (
          <div key={dino}>
            <Card>
              
              <Card.Body>
                <Card.Title>{dino.productName}</Card.Title>
                <Card.Text>
                  {dino.size},
                  {dino.diet},
                  {dino.price}
              </Card.Text>
              </Card.Body>
              <Button variant="primary">Add to cart</Button>
            </Card>
          </div>
        ))}
      </CardDeck>
    )
  }



  return (
    <div>
      <div className="banner-wrapper">
        <Image src={renderImageAndHeader()} fluid />
      </div>
      <div className="for-small-screen-display"><h1>{header}</h1></div>
      <div className="card-wrapper">
        {renderMultipleCards()}
      </div>
    </div>
  );
}

【问题讨论】:

  • getData 是异步操作(从 API 获取数据)。您不能调用它,然后只需映射下一行的数据。花一点时间研究 Javascript 中的同步与异步代码以及它们各自的含义。最重要的是,您在函数中调用renderMultipleCards(),调用getData(apiUrl) 的主体,执行API 请求然后设置状态。该状态更改将触发重新渲染,导致再次调用 renderMultipleCards() 并且循环将重新开始,无限循环。需要在组件挂载时调用一次,设置好数据后渲染

标签: node.js reactjs typescript express rest


【解决方案1】:

renderMultipleCards 正在发出一个副作用,即在渲染周期期间获取数据,然后更新状态。这会导致渲染循环。

您可能只需要在组件挂载时获取一次数据。使用安装 useEffect 挂钩(即空依赖项)来完成此操作。从renderMultipleCards 中删除getData(apiUrl) 调用。

useEffect(() => {
  getData(apiUrl);
}, []);

既然您的数据获取似乎依赖于apiUrl,那么您应该将apiUrl 作为组件状态的一部分并将其添加到效果的依赖数组中。我建议重构组件以创建地图/对象,以根据 window.location.href 值“查找”标题、api url 和图像源值。

const metaData = {
  ALL_DINOS: {
    header: "All Dinosaurs",
    apiUrl: ALL_DINOS_API_CALL,
    image: Omnivores
  },
  SMALL_DINOS: {
    header: "Small Dinosaurs",
    apiUrl: SMALL_DINOS_API_CALL,
    image: Small
  },
  MEDIUM_DINOS: {
    header: "Medium Dinosaurs",
    apiUrl: MEDIUM_DINOS_API_CALL,
    image: Medium
  },
  LARGE_DINOS: {
    header: "Large Dinosaurs",
    apiUrl: LARGE_DINOS_API_CALL,
    image: Large
  },
  CARNIVORE_DINOS: {
    header: "Carnivore Dinosaurs",
    apiUrl: CARNIVORE_DINOS_API_CALL,
    image: Carnivores
  },
  HERBIVORE_DINOS: {
    header: "Herbivore Dinosaurs",
    apiUrl: HERBIVORE_DINOS_API_CALL,
    image: Herbivores
  },
  OMNIVORE_DINOS: {
    header: "Omnivore Dinosaurs",
    apiUrl: OMNIVORE_DINOS_API_CALL,
    image: Omnivores
  }
};

export default function AllProducts() {
  const [dinos, setDinos] = useState < Array < any >> [];
  const [{ apiUrl, header, image }, setMeta] = useState({});

  React.useEffect(() => {
    // get metadata object or empty object fallback
    setMeta(metaData[window.location.href] ?? {});
  }, [window.location.href]);

  React.useEffect(() => {
    fetch(apiUrl, {
      method: "GET",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json"
      }
    })
      .then((response) => response.json())
      .then((data) => setDinos(data)) // <-- set dinos state directly
      .catch((err) => console.log(err));
  }, [apiUrl]); // <-- apiUrl state dependency

  function renderMultipleCards() {
    return (
      <CardDeck>
        {dinos.map((dino) => (
          <div key={dino}>
            <Card>
              <Card.Body>
                <Card.Title>{dino.productName}</Card.Title>
                <Card.Text>
                  {dino.size},{dino.diet},{dino.price}
                </Card.Text>
              </Card.Body>
              <Button variant="primary">Add to cart</Button>
            </Card>
          </div>
        ))}
      </CardDeck>
    );
  }

  return (
    <div>
      <div className="banner-wrapper">
        <Image src={image} fluid /> // <-- image state
      </div>
      <div className="for-small-screen-display">
        <h1>{header}</h1> // <-- header state
      </div>
      <div className="card-wrapper">{renderMultipleCards()}</div>
    </div>
  );
}

【讨论】:

  • 感谢您的回答!当我按照您的建议进行操作时,我收到错误“错误:重新渲染过多。React 限制了渲染次数以防止无限循环。”。您是否知道原因以及我该如何解决?
  • @ssh 啊,我明白了,是的,与renderImageAndHeader() 更新状态类似的问题。抱歉造成混淆,我会深入研究并尽快更新。
  • @ssh 我已经用更详尽的代码编辑建议更新了我的答案,请检查更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-09
  • 2013-06-18
  • 2013-01-01
  • 2018-05-14
  • 2016-09-05
  • 2015-01-12
相关资源
最近更新 更多