Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

120
Visualizações
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 Respostas
Responde à pergunta

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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda