#reactjs #firebase #google-cloud-firestore #react-hooks #use-effect
# #reactjs #firebase #google-облако-firestore #реагирующие хуки #использование-эффект
Вопрос:
Я не совсем понимаю, как работает функция очистки useEffect. Потому что, что бы я ни делал, я всегда получаю предупреждение:
Предупреждение: не удается выполнить обновление состояния реакции для размонтированного компонента. Это не операция, но это указывает на утечку памяти в вашем приложении. Чтобы исправить, отмените все подписки и асинхронные задачи в функции очистки useEffect.
Вот мой код:
useEffect(() => {
setLoading(true)
// Get position list
const getPositionList = db.collection('lists').doc('positions').get()
.then( res => {
let data = JSON.stringify(res.data())
data = JSON.parse(data)
setPositionsList(data.list)
setLoading(false)
})
return () => getPositionList
}, [])
Комментарии:
1. Вы меняете состояние размонтированного компонента, что не имеет смысла, и вы получаете за это предупреждение.
2. Не могли бы вы объяснить подробнее?
Ответ №1:
useEffect ожидает в качестве возвращаемого значения функцию отмены подписки / очистки или значение null. Your .get() не является подпиской, поэтому очищать нечего
НО useEffect НЕ является асинхронным, и .get() окончательно возвращает обещание, которое требует задержки, даже с .then() . Ваш массив зависимостей пуст, поэтому useEffect вызывается только один раз, но я подозреваю, что ваш компонент был отключен до того, как .get() когда-либо возвращался.
Ваш код должен быть ближе к:
useEffect(() => {
setLoading(true)
(async () => {
// Get position list
await db.collection('lists').doc('positions').get()
.then( res => {
let data = JSON.stringify(res.data())
data = JSON.parse(data)
setPositionsList(data.list)
setLoading(false)
});
)();
return null
}, [])
( async () => {})()
создает анонимную асинхронную функцию, затем выполняет ее. Вы хотите попытаться структурировать свою систему таким образом, чтобы включающий компонент не отключался перед загрузкой, для которого установлено значение false — мы этого здесь не видим, поэтому мы должны предположить, что вы делаете эту часть правильно.
Важные выводы:
- useEffect НЕ является асинхронным
- .get() ЯВЛЯЕТСЯ асинхронным
( async () => {})
создает анонимную асинхронную функцию и( async () => {})()
создает самоисполняющуюся анонимную асинхронную функцию
Ответ №2:
useEffect(() => {
setLoading(true)
// below firebase api return promise. hence no need to unsubscribe.
db.collection('lists').doc('positions').get()
.then( res => {
let data = JSON.stringify(res.data())
data = JSON.parse(data)
setPositionsList(data.list)
setLoading(false)
})
return () => {
// any clean up code, unsubscription goes here. unmounting phase
}
}, [])
Ответ №3:
Согласно ответу @Dennis Vash, я выясняю, что перед установкой состояния я должен проверить, смонтирован компонент или нет, поэтому я добавляю переменную useEffect
и перед установкой состояния проверяю, добавляю if
оператор:
useEffect(() => {
setLoading(true)
let mounted = false // <- add variable
// Get position list
db.collection('lists').doc('positions').get()
.then( res => {
let data = JSON.stringify(res.data())
data = JSON.parse(data)
if(!mounted){ // <- check is it false
setPositionsList(data.list)
setLoading(false)
}
})
return () => {
mounted = true // <- change to true
}
}, [])