Javascript Firestore onSnapshot通过OneEffect中的状态附加到阵列

Javascript Firestore onSnapshot通过OneEffect中的状态附加到阵列,javascript,reactjs,firebase,google-cloud-firestore,react-hooks,Javascript,Reactjs,Firebase,Google Cloud Firestore,React Hooks,我目前正在React中创建一个待办事项列表,该列表从Firestore检索任务,并使用状态挂钩将它们本地存储在数组中:const[todoList,setTodoList]=useState([])。我在编写此代码时遇到了一些障碍,主要是因为Firestore的onSnapshot功能似乎无法正确使用React。在加载(检索现有任务)和创建新任务时调用快照代码。用于将更改附加到阵列的快照代码为: todoReference.onSnapshot(colSnapshot => { c

我目前正在React中创建一个待办事项列表,该列表从Firestore检索任务,并使用状态挂钩将它们本地存储在数组中:
const[todoList,setTodoList]=useState([])
。我在编写此代码时遇到了一些障碍,主要是因为Firestore的
onSnapshot
功能似乎无法正确使用React。在加载(检索现有任务)和创建新任务时调用快照代码。用于将更改附加到阵列的快照代码为:

todoReference.onSnapshot(colSnapshot => {
    colSnapshot.docChanges().forEach(change => {
        if (change.type === 'added') {
            const taskData = change.doc.data();

            todoList.push(taskData);
        }
    });
    setTodoList(todoList); // "update state" using new array
});
当我尝试不同的版本时,会弹出一些问题(推到空数组,然后将两个数组连接在一起,等等):

  • 待办事项列表状态不会在新快照上保持。例如,创建一个新任务
    task2
    会将todoList更新为
    [task2]
    ,但创建另一个任务
    task3
    之后,会使第一个任务消失,并将数组更新为
    [task3]

  • onSnapshot继续检索相同的任务,尽管之前已检索到这些任务。例如,加载时,初始todoList将更新为
    [task1、task2、task3]
    。创建新任务并再次调用快照函数时,我希望todoList会更新为
    [task1,task2,task3,task4]
    。但是,我得到的是
    [task1,task2,task3,task1,task2,task3,task4]
    的一些变体,每当再次调用snapshot函数时,这些变体就会复合


这个问题似乎只发生在React中,而不是本机JavaScript中(任务的创建和检索没有任何重复)。我尝试过的一些解决方案是将所有内容包装在一个OneEffect中(我相信如果我没有将
作为依赖项传递给olist
,并且如果传递了,则会无限循环),并通过
unsubscribe()
调用快照函数(这仍然给了我第二个问题)

解决了!我将所有内容嵌套在一个useEffect中,没有依赖项,并设法解决了关于状态未正确更新的第一个要点。与通常使用
setTodoList(todoList.concat(newTasks))
设置状态不同,我使用
setTodoList(currentList=>currentList.concat(newTasks))
从功能上设置状态(关于setState是异步的关于useState,异步不是我的专长)。在这里找到答案:

以下是最终的快照更新代码(我还以某种方式解决了onSnapshot返回整个集合的问题,而不是每次都进行更改,但我忘记了如何进行更改,如果我记得原因,我将更新此代码):

useEffect(() => {
    let unsubscribe = todoReference.onSnapshot(colSnapshot => {
        console.log(colSnapshot.docChanges());
        let newTasks = [];
        colSnapshot.docChanges().forEach(change => {
            if (change.type === 'added') {
                const taskData = change.doc.data();

                newTasks.push(taskData);
            }
        });
        setTodoList(currentList => currentList.concat(newTasks));
    });
    return () => unsubscribe();
}, []);