Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

116
Vistas
Mix useEffect and Firebase's onValue

When working with Firebase and React, in order to fetch data based on state changes or on inner database changes (from another user for example), I often rely on pieces of code like this one:

    useEffect(() => {

    const getGamesInSelectedGroup = () => {

        if (!state.currentGroup) {
            return
        }

        const db = getDatabase();
        const resp = ref(db, `/games/${state.currentGroup.name}`);

        onValue(resp, (snap) => {
            if (snap.exists()) {
                const data = snap.val()
                const games = Object.keys(data).map(k => ({id: k, group: state.currentGroup.name, ...data[k]}))

                setState((prev) => ({
                    ...prev,
                    games: games,
                    isLoaded: true,
                }));
                return

            }
            setState((prev) => ({
                ...prev,
                games: null,
                isLoaded: true,
            }));
            toast.warning("no data for " + state.currentGroup.name)

        })
    }

    getGamesInSelectedGroup();

}, [state.currentGroup])

However, I am wondering if, whenever state.currentGroup changes, a new listener to /games/${state.currentGroup.name} is created? If so, is there a mean to unsubscribe to previous listener before creating a new one?

I have thinking about replacing onValue by a get call, still conditioned on state.currentGroup and using onValue outside useEffectto reflect "inner" database change.

about 4 years ago · Juan Pablo Isaza
1 Respuestas
Responde la pregunta

0

Rather than nest everything in your getGamesInSelectedGroup function (which is the pattern you would use for Promise-based APIs), just call it in the body of the useEffect to make managing the listener simpler:

useEffect(() => {
    if (!state.currentGroup) {
        return
    }

    const db = getDatabase();
    const resp = ref(db, `/games/${state.currentGroup.name}`);

    return onValue(resp, (snap) => { // <--- return the unsubscriber!
        if (snap.exists()) {
            const data = snap.val()
            const games = Object.keys(data)
                 .map(k => ({
                     id: k,
                     group: state.currentGroup.name,
                     ...data[k]
                 }));

            setState((prev) => ({
                ...prev,
                games, // you can use this instead of "games: games"
                isLoaded: true,
            }));
            return
        }
        setState((prev) => ({
            ...prev,
            games: null,
            isLoaded: true,
        }));
        toast.warning("no data for " + state.currentGroup.name)
    });
}, [state.currentGroup])

I would also recommend using a "snapshot to array of children" function rather than using Object.keys(snapshot.val()) to maintain the sort order from the query (it would be ignored using the code as-is). Unfortunately, at the time of writing, there is no equivalent of Firestore's QuerySnapshot#docs for the RTDB just yet. But it's pretty easy to make our own:

// returns array of DataSnapshot objects under this DataSnapshot
// put this outside of your component, like in a common function library file
const getSnapshotChildren = (snapshot) => {
    const children = [];
    // note: the curly braces on the next line are important! If the
    // callback returns a truthy value, forEach will stop iterating
    snapshot.forEach(child => { children.push(child) })
    return children;
}

useEffect(() => {
    if (!state.currentGroup) {
        return
    }

    const db = getDatabase();
    const resp = ref(db, `/games/${state.currentGroup.name}`);

    return onValue(resp, (snap) => { // <--- return the unsubscriber!
        if (snap.exists()) {
            const games = getSnapshotChildren(snap)
                 .map(child => ({
                     id: child.key,
                     group: state.currentGroup.name,
                     ...child.val()
                 }));

            setState((prev) => ({
                ...prev,
                games, // you can use this instead of "games: games"
                isLoaded: true,
            }));
            return
        }
        setState((prev) => ({
            ...prev,
            games: null,
            isLoaded: true,
        }));
        toast.warning("no data for " + state.currentGroup.name)
    });
}, [state.currentGroup])
about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda