【问题标题】:Mix useEffect and Firebase's onValue混合 useEffect 和 Firebase 的 onValue
【发布时间】:2022-02-07 00:32:49
【问题描述】:

在使用FirebaseReact 时,为了根据状态更改或内部数据库更改(例如来自另一个用户)获取数据,我经常依赖这样的代码:

    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])

但是,我想知道,每当state.currentGroup 更改时,是否会创建/games/${state.currentGroup.name} 的新侦听器?如果是这样,是否有办法在创建新的侦听器之前取消订阅以前的侦听器?

我正在考虑用get 调用替换onValue,仍然以state.currentGroup 为条件,并在useEffect 之外使用onValue 以反映“内部”数据库更改。

【问题讨论】:

    标签: javascript reactjs firebase firebase-realtime-database


    【解决方案1】:

    与其将所有内容都嵌套在您的 getGamesInSelectedGroup 函数中(这是您将用于基于 Promise 的 API 的模式),只需在 useEffect 的主体中调用它以简化侦听器的管理:

    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])
    

    我还建议使用“快照到子数组”函数而不是使用Object.keys(snapshot.val()) 来维护查询的排序顺序(使用代码原样会忽略它)。不幸的是,在撰写本文时,还没有与 Firestore 的 QuerySnapshot#docs 等效的 RTDB。但是我们自己制作很容易:

    // 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])
    

    【讨论】:

    • 因此,现在,通过返回退订者,我确定随时可以管理单个侦听器吗?
    • @floflo29 您最多只能有一个活跃的听众。上面的代码没有包含错误监听器。您可以参考 API 参考了解详细信息。
    • 好的,你确认,和我以前一样,在实例化的地方编写多个侦听器(即,每当调用 useEffect 时都会创建一个新侦听器)?
    • @floflo29 是的,在您之前的代码中,在每次渲染/更改状态时,您都会附加一个新的侦听器,但是因为您没有使用取消订阅功能来清理旧的侦听器,所以您会将它们堆叠在一起。
    猜你喜欢
    • 2022-10-01
    • 1970-01-01
    • 2023-03-26
    • 2022-01-12
    • 2021-08-09
    • 2020-11-25
    • 2020-03-23
    • 1970-01-01
    • 2020-02-17
    相关资源
    最近更新 更多