Choosing a React Hook suitable for use with Firestore's onsnapshot
P粉555682718
P粉555682718 2023-08-24 13:48:51
0
2
514
<p>I use Firestore snapshots a lot in my React Native app. I also used React hooks. The code looks like this: </p> <pre class="brush:php;toolbar:false;">useEffect(() => { someFirestoreAPICall().onSnapshot(snapshot => { // When the component initially loads, add all loaded data to the state. // When the data on Firestore changes, we receive updates in this callback, // Then update the UI based on the current state });; }, []);</pre> <p>At first, I thought <code>useState</code> was the best hook to store and update the UI. However, according to the settings of my <code>useEffect</code> hook, which comes with an empty dependency array, when the snapshot callback is fired and I try to modify the current state with new changes, the current state is undefined. I think this is due to closures. I was able to solve this problem by using <code>useRef</code> and <code>forceUpdate()</code> with the following code: </p> <pre class="brush:php;toolbar:false;">const dataRef = useRef(initialData); const [, updateState] = React.useState(); const forceUpdate = useCallback(() => updateState({}), []); useEffect(() => { someFirestoreAPICall().onSnapshot(snapshot => { // If snapshot data is added dataRef.current.push(newData) forceUpdate() // If the snapshot data is updated dataRef.current.find(e => some condition) = updatedData forceUpdate() });; }, []); return( // JSX using dataRef.current )</pre> <p>My question is, am I using <code>useRef</code> correctly as well as <code>forceUpdate</code> which is different from <code>useState</code>? I feel like updating the <code>useRef</code> hook and calling <code>forceUpdate()</code> throughout the application is not correct. When trying to use <code>useState</code>, I tried adding the state variable to the dependency array, but it resulted in an infinite loop. I only want to initialize the snapshot function once, and want the stateful data in the component to update over time as the backend changes (triggered in the onSnapshot callback). </p>
P粉555682718
P粉555682718

reply all(2)
P粉806834059

It will be better if you combine useEffect and useState. useEffect is responsible for setting and disabling the listener, and useState is only responsible for the data you need.

const [data, setData] = useState([]);

useEffect(() => { 
       const unsubscribe = someFirestoreAPICall().onSnapshot(snap => {
         const data = snap.docs.map(doc => doc.data())
         this.setData(data)
       });

       //记得在卸载组件时取消实时监听器,否则会造成内存泄漏
       return () => unsubscribe()
}, []);

You can then directly reference "data" in the useState hook in your application.

P粉165522886

A simple useEffect worked for me, i don't need to create a helper function or anything of sorts,

useEffect(() => {
        const colRef = collection(db, "data")
        //real time update
        onSnapshot(colRef, (snapshot) => {
            snapshot.docs.forEach((doc) => {
                setTestData((prev) => [...prev, doc.data()])
                // console.log("onsnapshot", doc.data());
            })
        })
    }, [])

A simple useEffect works for me, I don't need to create any helper functions or anything like that,

useEffect(() => {
        const colRef = collection(db, "data")
        //实时更新
        onSnapshot(colRef, (snapshot) => {
            snapshot.docs.forEach((doc) => {
                setTestData((prev) => [...prev, doc.data()])
                // console.log("onsnapshot", doc.data());
            })
        })
    }, [])
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template