如何正确使用 promises、Firebase 和 React 的 UseEffect 钩子?



基本上,我想要实现的是仅在变量不再未定义后运行一些代码。但是,我对使用承诺来等待火力基地响应以及使用 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>
)
}

根据代码中的注释,您的步骤3,4,5取决于LIST_OF_TIMES_FROM_DATABASE而且它也不是按顺序排列的,

并且您希望它按顺序执行,

1(首先,它将执行useEffect之外的所有事情,并且每当state发生变化时,它的依赖关系,所以请记住这一点

2(useEffect(() => {...},[]),一旦组件挂载,就会调用它,所以在大多数情况下,你会listOfTimes[],因为那时API调用可能是从服务器请求数据。

3(很少有不必要的东西,比如const [counts, setCounts] = useState({})useEffect(() => {...} , []),这是一种对功能的过度使用。

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


溶液:

因此,您可以做的是将所有代码放入函数displayListOfTimes并检查LIST_OF_TIMES_FROM_DATABASE是否可用,然后才继续执行步骤 3,4,5。所以你永远不会LIST_OF_TIMES_FROM_DATABASE未定义

注意:根据代码LIST_OF_TIMES_FROM_DATABASE要么为空白dataarray或数组,但永远不会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>
)
}

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

您可以在 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

相关内容

  • 没有找到相关文章

最新更新