Как заставить useEffect работать по порядку? - PullRequest
0 голосов
/ 11 апреля 2020

Моя цель - получить мой API и сделать несколько логов c с данными, когда компонент монтируется. Я создал еще один useEffect для запуска только тогда, когда первые вызовы извлечения успешны. Я могу получить все свои данные, но я получаю сообщение об ошибке "не могу прочитать свойство 'length' с нулевым значением". Если я правильно понял, второй useEffect будет запускаться всякий раз, когда изменяется companyData.

Ошибка:

 54 | useEffect(() => {
  55 |   async function organizeReviews() {
  56 |     let companyReviews = []
> 57 |       for(let i = 0; i < companyData.length; i++) {
     | ^  58 |         let reviewsForThisCo = []
  59 |         // loop through userReviews array
  60 |         for(let j=0; j < userReviews.length; j++) {

Код:

  const [averageRatings, setAverageRatings] = useState(null)
  const [companyData, setCompanyData] = useState(null)
  const [userReviews, setUserReviews] = useState([])
  const [organizedReviews, setOrganizedReviews] = useState(null)

  // Adding empty [] as second argument to run once component mounts
  useEffect(() => {
    async function getRatings() {
      try{
        const ratingRes = await fetch(process.env.REACT_APP_API_URL + '/api/v1/collected_reviews/')
        const ratingJson = await ratingRes.json()
        setAverageRatings(ratingJson.data)
      } catch(err) {
        console.log(err);
      }
    }

    async function getCompanyData() {
      try{
        const companyDataRes = await fetch(process.env.REACT_APP_API_URL + '/api/v1/companies/')
        const companyDataJson = await companyDataRes.json()
        setCompanyData(companyDataJson.data)
      } catch(err) {
        console.log(err);
      }
    }

    async function getCompanyReviews() {
      try{
        const reviewsRes = await fetch(process.env.REACT_APP_API_URL + '/api/v1/reviews/')
        const reviewsJson = await reviewsRes.json()
        setUserReviews(reviewsJson.data)
      } catch(err) {
        console.log(err);
      }
    }

    getRatings()
    getCompanyData()
    getCompanyReviews()

  }, [])

  useEffect(() => {
    async function organizeReviews() {
      let companyReviews = []
        for(let i = 0; i < companyData.length; i++) {
          let reviewsForThisCo = []
          // loop through userReviews array
          for(let j=0; j < userReviews.length; j++) {
            // if the user review id matches the company id
            if(userReviews[j].company.id === companyData[i].id) {
              reviewsForThisCo.push(userReviews[j])
            }
          }
            companyReviews.push(reviewsForThisCo)
        }
        setOrganizedReviews(companyReviews)
    }
    organizeReviews()
  }, [companyData])

Ответы [ 3 ]

0 голосов
/ 11 апреля 2020

Пожалуйста, используйте useRef () при втором использовании эффекта, как показано ниже

    const mounted = useRef();
      useEffect(() => {
      if (!mounted.current) {
        mounted.current = true;
      } else {
        async function organizeReviews() {
      let companyReviews = []
        for(let i = 0; i < companyData.length; i++) {
          let reviewsForThisCo = []
          // loop through userReviews array
          for(let j=0; j < userReviews.length; j++) {
            // if the user review id matches the company id
            if(userReviews[j].company.id === companyData[i].id) {
              reviewsForThisCo.push(userReviews[j])
            }
          }
            companyReviews.push(reviewsForThisCo)
        }
        setOrganizedReviews(companyReviews)
    }
    organizeReviews()
}
      }, [companyData])
0 голосов
/ 11 апреля 2020

Если я правильно понял, второй useEffect будет запускаться всякий раз, когда изменяется companyData.

yes, но когда второй useEffect пытается применить .length к "companyData", это все еще равно нулю, и вы не можете применить .length к нулевой переменной. эта ошибка останавливает выполнение до того, как «companyData» изменится

. Вам нужно инициализировать «companyData» как пустой массив

const [companyData, setCompanyData] = useState([])

Это более простой способ, или вы можете просмотреть его во втором использовании. Эффект "companyData" существует

useEffect(() => {
async function organizeReviews() {
  let companyReviews = []
    for(let i = 0; i < companyData.length; i++) {
      let reviewsForThisCo = []
      // loop through userReviews array
      for(let j=0; j < userReviews.length; j++) {
        // if the user review id matches the company id
        if(userReviews[j].company.id === companyData[i].id) {
          reviewsForThisCo.push(userReviews[j])
        }
      }
        companyReviews.push(reviewsForThisCo)
    }
    setOrganizedReviews(companyReviews)
}
if (companyData)
organizeReviews()
  }, [companyData])
0 голосов
/ 11 апреля 2020

второй useEffect будет запускаться при изменении companyData.

Это правда. Но он также всегда будет запускаться при первоначальном монтировании компонента. Поэтому вы должны убедиться, что ваш companyData существует внутри вашего второго useEffect, так как он не будет существовать при первом рендере:

  useEffect(() => {

    if (companyData){
      async function organizeReviews() {
        let companyReviews = []
          for(let i = 0; i < companyData.length; i++) {
            let reviewsForThisCo = []
            // loop through userReviews array
            for(let j=0; j < userReviews.length; j++) {
              // if the user review id matches the company id
              if(userReviews[j].company.id === companyData[i].id) {
                reviewsForThisCo.push(userReviews[j])
              }
            }
              companyReviews.push(reviewsForThisCo)
          }
          setOrganizedReviews(companyReviews)
      }
      organizeReviews()
    } // if (companyData)

  }, [companyData])

Теперь он будет работать при первом рендере, но сделайте ничего, поскольку companyData еще не существует. Затем он будет запускаться каждый раз, когда companyData меняется.

...