¿Qué gancho de reacción usar con la instantánea de Firestore?

Resuelto Kevin Quiring asked hace 54 años • 4 respuestas

Utilizo muchas instantáneas de Firestore en mi aplicación nativa de reacción. También estoy usando ganchos de React. El código se parece a esto:

useEffect(() => {
    someFirestoreAPICall().onSnapshot(snapshot => {

        // When the component initially loads, add all the loaded data to state.
        // When data changes on firestore, we receive that update here in this
        // callback and then update the UI based on current state

    });;
}, []);

Al principio supuse useStateque sería el mejor gancho para almacenar y actualizar la interfaz de usuario. Sin embargo, según la forma en que useEffectestá configurado mi enlace con una matriz de dependencia vacía, cuando la devolución de llamada de la instantánea se activa con datos actualizados e intento modificar el estado actual con los nuevos cambios, el estado actual no está definido. Creo que esto se debe a un cierre. Puedo solucionarlo usando useRefalgo forceUpdate()así:

const dataRef = useRef(initialData);

const [, updateState] = React.useState();
const forceUpdate = useCallback(() => updateState({}), []);

useEffect(() => {
    someFirestoreAPICall().onSnapshot(snapshot => {

       // if snapshot data is added
       dataRef.current.push(newData)
       forceUpdate()

       // if snapshot data is updated
       dataRef.current.find(e => some condition) = updatedData
       forceUpdate()

    });;
}, []);

return(
// JSX that uses dataRef.current directly
)

Mi pregunta es: ¿estoy haciendo esto correctamente usando useRefjunto con a forceUpdateen lugar de hacerlo useStatede otra manera? No parece correcto tener que actualizar un useRefenlace y llamar forceUpdate()a toda mi aplicación. Al intentarlo, useStateintenté agregar la variable de estado a la matriz de dependencia pero terminé con un bucle infinito. Solo quiero que la función de instantánea se inicialice una vez y que los datos con estado en el componente se actualicen con el tiempo a medida que las cosas cambian en el backend (que se activa en la devolución de llamada onSnapshot).

Kevin Quiring avatar Jan 01 '70 08:01 Kevin Quiring
Aceptado

Sería mejor si combinaras useEffect y useState. UseEffect configurará y desconectará el oyente, useState puede simplemente ser responsable de los datos que necesita.

const [data, setData] = useState([]);

useEffect(() => { 
       const unsubscribe = someFirestoreAPICall().onSnapshot(snap => {
         const data = snap.docs.map(doc => doc.data())
         this.setData(data)
       });

       //remember to unsubscribe from your realtime listener on unmount or you will create a memory leak
       return () => unsubscribe()
}, []);

Luego puede simplemente hacer referencia a "datos" desde el enlace useState en su aplicación.

Josh Pittman avatar Jan 29 '2020 01:01 Josh Pittman

Un simple useEffect funcionó para mí, no necesito crear una función auxiliar ni nada por el estilo,

useEffect(() => {
        const colRef = collection(db, "data")
        //real time update
        onSnapshot(colRef, (snapshot) => {
            snapshot.docs.forEach((doc) => {
                setTestData((prev) => [...prev, doc.data()])
                // console.log("onsnapshot", doc.data());
            })
        })
    }, [])
anshul avatar Feb 08 '2022 15:02 anshul

Descubrí que dentro del método onSnapshot() no podía acceder al estado (por ejemplo, si accedo a console.log(state) obtendría un valor vacío.

La creación de una función auxiliar funcionó, pero no estoy seguro de si se trata de una solución pirateada o no, sino algo como:

[state, setState] = useState([])

stateHelperFunction = () => {
//update state here
setState()
}

firestoreAPICall.onSnapshot(snapshot => {
stateHelperFunction(doc.data())
})
Tepinvic avatar May 25 '2020 15:05 Tepinvic