React GET API调用中的无限循环以及使用Hook和useState()的正确方法

时间:2020-08-06 10:25:14

标签: javascript reactjs mongodb axios

我是React的新手,我正在开发对Express.js和MongoDB远程后端进行标准CRUD操作的应用程序。

在一个页面中,我需要显示从GET API调用到使用Axios进行的远程服务器的值。每个对象都作为多个字段,以及 company 字段(例如 Exhibitor 列中的值,例如 5f280eb605c9b25cfeee285c )对应于另一个集合中另一个对象的 _id Mongo对象字段值。

我需要从表中恢复原始值,进行另一个API调用,并从具有该对象的对象中获取 name 字段(例如 Company name example string ) _ID。之后,我需要在表格字段而不是_id中显示它。

例如,为了更加清楚,将item.company字段 5f27e8ee4653de50faeb1784 显示为公司名称示例字符串

我还需要对 Status 列执行相同的操作(但不对远程服务器进行GET API调用),在该列中,我需要根据 item.active 显示图标>值,即布尔值。

这不需要任何按钮即可完成,但是当我自动打开页面时。

我已经制作了一个标准的javascript函数来做到这一点,但是我想这是一个无限循环,因为React每次渲染时都会调用该函数。

执行此操作的正确方法是什么?

这是循环后控制台中的错误

xhr.js:178 GET http://myserver.com/companies/5f280eb605c9b25cfeee285c net :: ERR_INSUFFICIENT_RESOURCES

import React, { useState, useEffect, useCallback } from 'react'
import { Tab, Tabs, Col, Form, Button } from 'react-bootstrap'
import { FiTrash, FiCloud, FiPhoneCall, FiUserCheck, FiUserX, FiEye } from 'react-icons/fi'
import axios from 'axios'

const EventProfile = (props) => {

  // SOME CODE HERE //

  //GET STANDS FROM DB

  const [allStands, viewStands] = useState([{}]);

  useEffect(() => {
      const id = props.match.params.id
      const fetchStands = async () => {
      const response = await axios.get(`http://myserver.com/stands/${id}`);
      viewStands(response.data);
    }
    fetchStands();
  }, [])


    // RECOVER NAME USING THE COMPANY ID FROM ANOTHER COLLECTION

    const [companyNameGetted, getCompanyName] = useState({})

  const getCompanyFromId = useCallback((props) => {
    const id = props;
    const getCompany = async () => {
      const response = await axios.get(`http://myserver.com/companies/${id}`);
      getCompanyName(response.data);
    }
    getCompany([]);
  }, [])

    // DISPLAY ICON DEPENDING ON OBJECT active FIELD


      const handleStandStatus = (status) => {
    if(status === true) {
      return <FiCloud style={{color: "green"}}/>;
    } else {
      return <FiCloud style={{color: "grey"}} description="Offline"/>;
    }
  }
   

   // OTHER CODE HERE //
return (

     //SOME CODE HERE//
    
             <Tab eventKey="stands" title="Stands">
          <div className="py-12 w-full">
            <table className="table table-lg">
              <thead>
                <tr>
                  <th>Status</th>
                  <th>Name</th>
                  <th>Exhibitor</th>
                  <th>Size</th>
                  <th>Color Palette</th>
                </tr>
              </thead>
              <tbody>
                {allStands.map((item, index) =>{
                  return(
                    <tr key={index}>
                      <td>{handleStandStatus(item.active)}</td>
                      <td><Link to={`/standProfile/${item._id}`}>{item.name}</Link></td>
                      <td>{getCompanyFromId(item.company)}<Link to={`/companyProfile/${item.company}`}><span>{companyNameGetted.name}</span></Link></td>
                      <td>{item.size}</td>
                      <td>{item.colorPalette}</td>
                    </tr>
                  )
                  })}
              </tbody>
            </table>
          </div>
        </Tab>


     // OTHER CODE HERE //

 )
}

export default EventProfile

1 个答案:

答案 0 :(得分:1)

这可能是无限循环的原因:

<td>{getCompanyFromId(item.company)}<Link to={`/companyProfile/${item.company}`}><span>{companyNameGetted.name}</span></Link></td>

因为您在组件的返回范围内调用了一个函数,然后该函数将调用getCompany函数,该函数将更新您的companyNameGetted状态。

在您的组件return上引用了companyNameGetted状态,因此调用getCompanyFromId将导致重新渲染,这将获取公司,更改状态,重新渲染等,从而在无限循环中。

获得所有展台后,您可以在useEffect中获取公司,也可以设置一个

useEffect(() => {get all company from allStands}, [allStands]);

因此它将反映allStands状态的变化。

编辑:这是一个进一步描述我的意思的示例。

const EventProfile = props => {
  // usually you'll want to name the variables as so:
  // a noun/object for the first one (stands)
  // a setter for the second one, since it is a function to set the `stands`
  const [stands, setStands] = useState([]);
  const [companies, setCompanies] = useState({});

  // usual useEffect that'll be triggered on component load, only one time
  useEffect(() => {
    const fetchStands = async () => {
      const response = await axios.get("stands url here");
      setStands(response.data);
    };
    fetchStands();
  }, []);

  //another useEffect that'll be triggered when there's a change in the dependency array given, i.e. the `stands` variable. so, it'll fetch company names whenever the `stands` state changes.
  useEffect(() => {
    const fetchCompanies = async () => {
      const newCompanies = {...companies};
      // wait for all company names have been retrieved
      await Promise.all(stands.forEach(s => {
        const id = s.company;
        const response = await axios.get("company url here with " + id);
        newCompanies[id] = response.data;
      }));
      setCompanies(newCompanies);
    };
    fetchCompanies();
  }, [stands]);

  return (
    // ... some components
    {stands.map((item, index) => (
      <tr key={index}>
        <td><Link to={`/some/url/${item.company}`}>{companies[item.company]}</Link></td>
      </tr>
    )}
  );
}