【问题标题】:How to properly work with promises, Firebase and React's UseEffect hook?如何正确使用 Promise、Firebase 和 React 的 UseEffect 挂钩?
【发布时间】:2020-09-25 00:46:48
【问题描述】:

基本上,我想要实现的是仅在变量不再未定义之后才运行一些代码。但是,我对使用 promise 来等待 firebase 响应以及使用 React 的 useEffect 挂钩感到非常困惑。

我做了一些研究,也许听众是我的答案?不过,我真的不知道如何实现它们。

const weekday = moment().isoWeekday() 

export const TimeGrid = () => {
  const { userObject } = useContext(Context)

  //(1) I wish to use the below to generate a list of times
  const [LIST_OF_TIMES_FROM_DATABASE, SET_LIST_OF_TIMES_FROM_DATABASE] = useState([])

  let businessId

  function getBusinessId() {
    if (userObject) businessId = userObject.businessId
  }

  getBusinessId()

  //defines function that will run below inside of first useEffect
  //also where the list of times is supposed to be set
  function getWorkingHoursAccordingToDayOfTheWeek(day, snapshot) {
    const workingHours = []
    const ALL_AVAILABLE_TIMES = []

    workingHours.push(snapshot.data().beginWeek)
    workingHours.push(snapshot.data().endWeek)

    const [begin, ending] = workingHours
    let bgn = moment(begin, 'HH:mm')
    const end = moment(ending, 'HH:mm')
    let i = 0
    while (bgn <= end) {
      ALL_AVAILABLE_TIMES.push({
        id: i,
        type: 'time',
        day: 'today',
        time: bgn.format('HHmm'),
      })
      bgn = bgn.add(30, 'minutes')
      i++
    }
    SET_LIST_OF_TIMES_FROM_DATABASE(ALL_AVAILABLE_TIMES) //(2) This is where I set the list, which won't work on its 1st run
  }

  useEffect(() => {
    async function getTimesFromDB() {
      const approved = await approvedBusinessService.doc(businessId)
      const snapshotApproved = await approved.get()
      if (snapshotApproved.exists) {
        getWorkingHoursAccordingToDayOfTheWeek(weekday, snapshotApproved)
        return
      } else {
        const pending = await businessPendingApprovalService.doc(businessId)
        const snapshotPending = await pending.get()
        if (snapshotPending.exists) {
          getWorkingHoursAccordingToDayOfTheWeek(weekday, snapshotPending)
        }
      }
      return
    }

    getTimesFromDB()
  }, [userObject])

  const allBookedTimes = allBookings.map(element => element.time)
  //the below used to be 'listOfTimesJSON.map(item => item.time)' and used to work as it was a static JSON file
  //(3) this is sometimes undefined... so all of the code below which relies on this will not run
  const listOfTimes = LIST_OF_TIMES_FROM_DATABASE.map(item => item.time)

  const [counts, setCounts] = useState({})
  useEffect(() => {
    const counts = {}
    //(4) below will not work as listOfTimes won't exist (because of 'LIST_OF_TIMES_FROM_DATABASE' being undefined)
    listOfTimes.forEach(x => {
      counts[x] = (counts[x] || 0) + 1 

      if (allBookedTimes.includes(x)) counts[x] = counts[x] - 1
    })
    setCounts(counts)
  }, [])

  const sortedListOfTimesAndCount = Object.keys(counts).sort() //(5) undefined, because 'counts' was undefined.
  const displayListOfTimes = sortedListOfTimesAndCount.map(
    time =>
      time > currentTime && (
        <>
          <li>
            {time}
          </li>
        </>
      ),
  )

  return (
    <div>
      <ul>{displayListOfTimes}</ul>
    </div>
  )
}

【问题讨论】:

    标签: javascript reactjs firebase google-cloud-firestore react-hooks


    【解决方案1】:

    根据您代码中的 cmets,您的步骤 3 ,4 ,5 取决于 LIST_OF_TIMES_FROM_DATABASE 而且也不按顺序,

    你期望它按顺序执行,

    1) 首先,它将执行useEffect 之外的所有内容,并且每当state 发生变化时,它都是依赖项,因此请记住这一点

    2) useEffect(() =&gt; {...},[]) 这将在组件安装后立即被调用,因此在大多数情况下,您将得到 listOfTimes[],因为那时 API 调用可能是在从服务器请求数据。

    3) 很少有像const [counts, setCounts] = useState({})useEffect(() =&gt; {...} , []) 这样不必要的东西,这是对功能的过度使用。

    有时你可以简单地做一些事情,但随着所有这些事情变得复杂


    解决方案:

    因此,您只需将所有代码放入 displayListOfTimes 函数中,然后检查 LIST_OF_TIMES_FROM_DATABASE(如果可用),然后继续执行步骤 3、4、5。所以你永远不会得到LIST_OF_TIMES_FROM_DATABASE undefined

    注意:根据代码 LIST_OF_TIMES_FROM_DATABASE 要么为空白 array 或带有 data 但从不使用 undefined 的数组,如果您未定义,那么您可以在设置状态时检查它

    const weekday = moment().isoWeekday() 
    
    export const TimeGrid = () => {
      const { userObject } = useContext(Context)
    
      //(1) I wish to use the below to generate a list of times
      const [LIST_OF_TIMES_FROM_DATABASE, SET_LIST_OF_TIMES_FROM_DATABASE] = useState([])
    
      let businessId
    
      function getBusinessId() {
        if (userObject) businessId = userObject.businessId
      }
    
      getBusinessId()
    
      //defines function that will run below inside of first useEffect
      //also where the list of times is supposed to be set
      function getWorkingHoursAccordingToDayOfTheWeek(day, snapshot) {
        const workingHours = []
        const ALL_AVAILABLE_TIMES = []
    
        workingHours.push(snapshot.data().beginWeek)
        workingHours.push(snapshot.data().endWeek)
    
        const [begin, ending] = workingHours
        let bgn = moment(begin, 'HH:mm')
        const end = moment(ending, 'HH:mm')
        let i = 0
        while (bgn <= end) {
          ALL_AVAILABLE_TIMES.push({
            id: i,
            type: 'time',
            day: 'today',
            time: bgn.format('HHmm'),
          })
          bgn = bgn.add(30, 'minutes')
          i++
        }
        SET_LIST_OF_TIMES_FROM_DATABASE(ALL_AVAILABLE_TIMES) //(2) This is where I set the list, which won't work on its 1st run
      }
    
      useEffect(() => {
        async function getTimesFromDB() {
          const approved = await approvedBusinessService.doc(businessId)
          const snapshotApproved = await approved.get()
          if (snapshotApproved.exists) {
            getWorkingHoursAccordingToDayOfTheWeek(weekday, snapshotApproved)
            return
          } else {
            const pending = await businessPendingApprovalService.doc(businessId)
            const snapshotPending = await pending.get()
            if (snapshotPending.exists) {
              getWorkingHoursAccordingToDayOfTheWeek(weekday, snapshotPending)
            }
          }
          return
        }
    
        getTimesFromDB()
      }, [userObject])
    
    
      const displayListOfTimes = () => { // <----- Start - HERE
        if(LIST_OF_TIMES_FROM_DATABASE && LIST_OF_TIMES_FROM_DATABASE.length) {
          const counts = {}
          const allBookedTimes = allBookings.map(element => element.time)
    
          //(3) ------ this will never be undefined
          const listOfTimes = LIST_OF_TIMES_FROM_DATABASE.map(item => item.time)
    
          //(4) ------ now it will work always
          listOfTimes.forEach(x => {
            counts[x] = (counts[x] || 0) + 1 
    
            if (allBookedTimes.includes(x)) counts[x] = counts[x] - 1
          })
    
          //(5) ------ now it will work 
          const sortedListOfTimesAndCount = Object.keys(counts).sort() 
          return sortedListOfTimesAndCount.map(
            time =>
              time > currentTime && (
                <>
                  <li>
                    {time}
                  </li>
                </>
              ),
          )
        } else {
          return <li>Nothing to show for now</li>
        }
      }
    
      return (
        <div>
          <ul>{displayListOfTimes}</ul>
        </div>
      )
    }
    

    【讨论】:

    【解决方案2】:

    问题是因为在获取请求后 listOfTimes 值更新时,没有调用使用 listOfTimes 的 useEffect。

    您可以在 useEffect 中定义listOfTimes 并添加对LIST_OF_TIMES_FROM_DATABASE 的依赖

      const [counts, setCounts] = useState({})
      useEffect(() => {
        // Defining it inside because if we don't and add allBookedTimes as a dependency then it will lead to an infinite look since references will change on each re-render
    
        const allBookedTimes = allBookings.map(element => element.time)
        const listOfTimes = LIST_OF_TIMES_FROM_DATABASE.map(item => item.time)
        const counts = {}
        listOfTimes.forEach(x => {
          counts[x] = (counts[x] || 0) + 1 
    
          if (allBookedTimes.includes(x)) counts[x] = counts[x] - 1
        })
        setCounts(counts)
      }, [LIST_OF_TIMES_FROM_DATABASE, allBookings]); // Add both dependencies. This will ensure that you update counts state when the state for times changes
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-02-03
      • 2020-09-15
      • 1970-01-01
      • 2021-02-13
      • 1970-01-01
      • 2021-03-31
      • 2021-11-11
      • 1970-01-01
      相关资源
      最近更新 更多