【问题标题】:How to properly update state with Firebase and useEffect()?如何使用 Firebase 和 useEffect() 正确更新状态?
【发布时间】:2020-03-01 15:52:52
【问题描述】:

我正在尝试从 Firebase 获取数据,然后将其推送到我的状态。目标是创建一种机制,每次 Firebase 中的数据发生变化时都会这样做。

我使用了on() 方法和useEffect()。不幸的是,即使状态发生变化,React 也不会重新渲染我的组件。 (忽略直接引用,仅供测试)

const [tasks, setTasks] = useState([]);

    useEffect(() => {
        const fetchedTasks = [];
        const ref = props.firebase.db.ref('users/1zfRCHmD4MVjJj7L884LL4TMwAH3');
        const listener = ref.on('value', snapshot => {
            snapshot.forEach(childSnapshot => {
                const key = childSnapshot.key;
                const data = childSnapshot.val();
                fetchedTasks.push({ id: key, ...data });
            });
            setTasks(fetchedTasks);
        });
        return () => ref.off('value', listener);
    }, []);

此时,当我在 Firebase 控制台中手动更改我的数据时,我可以在我的开发工具中看到它触发了侦听器,获取了数据,但它与之前的状态(任务)合并了。

我当然希望它取代以前的状态。其次,组件不会重新渲染。我知道空的依赖项列表对此负责(“[]”)并且组件只安装一次,但是当我删除列表时,组件正在更新并迅速冻结我的浏览器。我也试过"[tasks]",结果是相似的——组件一遍又一遍地重新渲染。 Firebase 由上下文提供,ESLint 显示:

“React Hook useEffect 缺少依赖项:'props.firebase.db'。要么包含它,要么删除依赖项数组。”

当我这样做时,它仍然无法工作并且组件没有更新。

【问题讨论】:

  • 我认为它被合并的原因是您只在安装一次时创建了fetchedTasks 数组。之后,每次你说 fetchedTasks.push(..) 时,你都会添加到同一个数组中。所以这个列表变得越来越长。您可能希望在每次获取值时重置fetchedTasks ,或者使用.map 而不是forEach,然后调用setTasks,返回值为.map
  • 非常感谢!这实际上解决了我的问题!你给了我一个很好的暗示。我再次查看了我的代码,很快意识到fetchedTasks 应该直接在快照回调函数中声明。然后我把props.firebase.db 放在依赖列表中。一切都更新了,没有性能问题!

标签: javascript reactjs firebase firebase-realtime-database


【解决方案1】:

由于我的评论解决了问题,以下是官方答案:

你的状态总是被合并的原因是你的变量fetchedTasks只在钩子useEffect在Mount上运行时被声明一次。之后,每次您的 firebase 数据库更新新值并调用 fetchedTasks.push(...) 时,您都在推送相同的“旧”数组。因此,您的列表越来越长。

一种解决方案是重新声明fetchedTasks,或将其设置回一个空数组。

const [tasks, setTasks] = useState([]);

    useEffect(() => {
        const ref = props.firebase.db.ref('users/1zfRCHmD4MVjJj7L884LL4TMwAH3');
        const listener = ref.on('value', snapshot => {
            const fetchedTasks = [];
            snapshot.forEach(childSnapshot => {
                const key = childSnapshot.key;
                const data = childSnapshot.val();
                fetchedTasks.push({ id: key, ...data });
            });
            setTasks(fetchedTasks);
        });
        return () => ref.off('value', listener);
    }, [props.firebase.db]);

另一种方法是改用.map,并使用setTasks 的返回值。我认为这是我的首选:

const [tasks, setTasks] = useState([]);

    useEffect(() => {
        const ref = props.firebase.db.ref('users/1zfRCHmD4MVjJj7L884LL4TMwAH3');
        const listener = ref.on('value', snapshot => {
            const fetchedTasks = snapshot.map(childSnapshot => {
                const key = childSnapshot.key;
                const data = childSnapshot.val();
                return { id: key, ...data };
            });
            setTasks(fetchedTasks);
        });
        return () => ref.off('value', listener);
    }, [props.firebase.db]);

请注意,您必须将 props.firebase.db 传递给您的依赖数组,以消除 eslint 警告。对props.firebase.db 的引用永远不会改变,因此应该保存以在依赖数组中声明它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-07
    • 2022-01-24
    • 2022-01-04
    • 2020-04-14
    • 1970-01-01
    • 2020-09-25
    • 2021-02-11
    相关资源
    最近更新 更多