Estoy trabajando en una aplicación web de clonación de Netflix y he almacenado algunos datos en Firestore para hacer una lista de favoritos. Cuando obtuve datos de Firestore e intenté almacenarlos en un estado, ocurrió un error.
Había alrededor de cuatro documentos en Firebase, pero solo obtenía el último cada vez que lo intentaba.
Incluí la clave del mapa e intenté dar la cadena vacía de estado como valor inicial, probé el operador de propagación pero ninguno de ellos funcionó
firestore está funcionando bien, ya que veo esos documentos en la consola
const [movie, setMovie] = useState([]) useEffect(() => { async function fetchData() { const q = query(collection(db, "movie")); const querySnapshot = await getDocs(q); querySnapshot.forEach((doc) => { setMovie([...movie, doc.data().Details]) console.log(doc.data().Details) console.log(movie) }); } fetchData()Su función fetchData se cierra sobre movie , lo que significa que solo ve la movie que existía cuando se creó la función (no ha mostrado lo suficiente como para estar seguro, pero supongo que tiene una matriz de dependencias vacía en ese useEffect , por lo que sería ser una movie siempre vacía). Hacer setMovie([...movie, doc.data().Details]) simplemente extiende esa movie vacía y luego agrega el documento final.
En cambio, dos cosas:
Siempre que actualice el estado en función del estado existente, es mejor usar el formulario de devolución de llamada del setter para obtener la versión actualizada del estado que está actualizando.
Reúna los documentos y luego haga una sola llamada de setter.
const [movies, setMovies] = useState([]) useEffect(() => { async function fetchData() { const q = query(collection(db, "movie")); const querySnapshot = await getDocs(q); setMovies(previousMovies => [ ...previousMovies, ...querySnapshot.map((doc) => doc.data().Details) ]); } fetchData(); }, []); // <== I've assumed this(Tenga en cuenta que lo he puesto en plural, ya que hay más de una película).
(No uso MongoDB, pero asumí por forEach que querySnapshot es una matriz y, por lo tanto, tiene map . Si no, es bastante fácil crear la matriz, use forEach para push y luego haga setMovies llamar.)
Pero hay otra cosa: debe permitir la posibilidad de que su componente se desmonte antes de que se complete la consulta. Si su getDocs tiene una forma de cancelar su operación, querrá usarla. Por ejemplo, si aceptó una AbortSignal , podría verse así:
const [movies, setMovies] = useState([]) useEffect(() => { async function fetchData() { const controller = new AbortController(); const { signal } = controller; const q = query(collection(db, "movie")); const querySnapshot = await getDocs(q, signal); if (!signal.aborted) { setMovies(previousMovies => [ ...previousMovies, ...querySnapshot.map((doc) => doc.data().Details) ]); } } fetchData(); return () => { controller.abort(); }; }, []);Pero si hay algún otro mecanismo de cancelación, úsalo naturalmente.
Si no hay un mecanismo de cancelación, puede usar una bandera para no intentar usar el resultado cuando no se usará para nada, pero eso puede ser excesivo. (React ha dejado de quejarse cuando realiza una actualización de estado después de desmontar el componente, ya que generalmente era algo benigno).
Para empezar, te sugiero que hagas algo como lo siguiente:
const [movie, setMovie] = useState([]) useEffect(() => { async function fetchData() { const q = query(collection(db, "movie")); const querySnapshot = await getDocs(q); setMovie(querySnapshot.flatMap(elt=>elt.data().Details)); } fetchData()Es porque actualiza su estado en forEach loop, comienza desde el primer documento hasta el último, al final configura la última película, es por eso que siempre tiene el último documento en su estado.
En primer lugar, no actualice el estado con demasiada frecuencia, ya que crea problemas de rendimiento de la aplicación.
Para solucionarlo (según tu ejemplo):
let movies=[] querySnapshot.forEach((doc) => { movies([...movies, doc.data().Details]); //or movies.push(doc.data().Details); }); setMovie(movies);