как отменить подписки на firebase с эффектом использования

#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 
        }
    }, [])