¿Qué gancho de reacción usar con la instantánea de Firestore?
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 useState
que sería el mejor gancho para almacenar y actualizar la interfaz de usuario. Sin embargo, según la forma en que useEffect
está 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 useRef
algo 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 useRef
junto con a forceUpdate
en lugar de hacerlo useState
de otra manera? No parece correcto tener que actualizar un useRef
enlace y llamar forceUpdate()
a toda mi aplicación. Al intentarlo, useState
intenté 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).
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.
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());
})
})
}, [])
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())
})