【问题标题】:How do I make useEffect run in order?如何使 useEffect 按顺序运行?
【发布时间】:2020-07-23 17:53:42
【问题描述】:

我的目标是获取我的 API 并在组件挂载时对数据执行一些逻辑。我创建了另一个 useEffect 仅在第一次 fetch 调用成功时运行。我能够获取所有数据,但出现错误“无法读取 null 的属性‘长度’”。如果我理解正确,第二个 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])

【问题讨论】:

  • 你试过console.logcompanyData里面使用Effect吗?我想你会发现它在挂载时设置为null 时会被触发。

标签: reactjs fetch use-effect


【解决方案1】:

第二个 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 更改时运行。

【讨论】:

    【解决方案2】:

    请在第二个 useEffect 中使用 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])
    

    【讨论】:

    • 通过添加 useRef() 我们确保在组件挂载时我们只设置 mount.current=true 并且在组件更新时我们调用了 OrganizeReviews() 函数
    【解决方案3】:

    如果我理解正确,第二个 useEffect 将在 companyData 更改时运行。

    是的,但是当第二个 useEffect 尝试将 .length 应用于“companyData”时,这仍然为 null,并且您不能将 .length 应用于 null 变量。此错误会在“companyData”更改之前停止执行

    你要做的就是将“companyData”初始化为一个空数组

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

    这是比较简单的方法,或者你可以查看里面的第二个useEffect是否存在“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])

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-15
      • 1970-01-01
      • 1970-01-01
      • 2015-12-21
      • 2015-09-21
      相关资源
      最近更新 更多