Search code examples
reactjsfirebasefirebase-realtime-databaseuse-effect

Use useEffect in react functional class to write data to firebase


The code persistently Data stores in Firebase which is what I wanted after each session, but it's hacky because setOnes([...ones,1]) and writeToFirebase shouldn't be called under the same functions due to async, but every time I tried with useEffect(()=>writeToFirebase(),[setOnes]) to achieve similar result as ComponentDidUpdate, it got triggered right at the beginning of the launch and reset to FB database back to [1,1]. This is the code I have, codesandbox link: https://codesandbox.io/s/super-simple-react-firebase-functional-class-hook-set-up-eyo15?file=/src/App2.js:279-326

const App2 = () =>{
  
  const [ones, setOnes] = useState([1,1]);
  useEffect(() => {
    Firebase.initializeApp(config)
    getFromFirebase()
  }, []);

  // useEffect(()=>writeToFirebase(),[setOnes])

  const writeToFirebase = () => {
    Firebase.database()
      .ref("/")
      .set(ones);
    console.log("DATA SAVED");
  };

  const getFromFirebase = () => {
    let ref = Firebase.database().ref("/");
    ref.on("value", snapshot => {
      const state = snapshot.val();
      setOnes(state)
    })
  }

  const handleAdd =() => {
    setOnes([...ones,1])
    writeToFirebase()
  }
    
    const list = ones.map((e,i)=>( 
        <div
            key={e.id}
        > {e} </div>
    ))
  
    return ( 
    <div>
      {list}
      <button onClick={handleAdd}>Add</button>
    </div> 
    )
}

export default App2;

Solution

  • Do not use setones as a dependency in useEffect, use ones. Full Code will look like this

    const App2 = () =>{
      
      const [ones, setOnes] = useState([1,1]);
      useEffect(() => {
        Firebase.initializeApp(config)
        getFromFirebase()
      }, []);
    
      useEffect(()=>writeToFirebase(),[ones])
    
      const writeToFirebase = () => {
        Firebase.database()
          .ref("/")
          .set(ones);
        console.log("DATA SAVED");
      };
    
      const getFromFirebase = () => {
        let ref = Firebase.database().ref("/");
        ref.on("value", snapshot => {
          const state = snapshot.val();
          setOnes(state)
        })
      }
    
      const handleAdd =() => {
        setOnes([...ones,1])
      }
        
        const list = ones.map((e,i)=>( 
            <div
                key={e.id}
            > {e} </div>
        ))
      
        return ( 
        <div>
          {list}
          <button onClick={handleAdd}>Add</button>
        </div> 
        )
    }
    
    export default App2;
    

    Altenatively, if you want to call writeToFirebase() only when ones change and not on initial render, you can use a custom hook useDidUpdateEffect. You can find the code in the answer to this question React hooks useEffect only on update?. In that case the full code will look like this

    const App2 = () =>{
      
      const [ones, setOnes] = useState([1,1]);
      const isInitialMount = useRef(true);
      useEffect(() => {
        Firebase.initializeApp(config)
        getFromFirebase()
      }, []);
    
      
      useEffect(() => {
      if (isInitialMount.current) {
           isInitialMount.current = false;
         } else {
         writeToFirebase();
        }
      }, [ones]);
    
      const writeToFirebase = () => {
        Firebase.database()
          .ref("/")
          .set(ones);
        console.log("DATA SAVED");
      };
    
      const getFromFirebase = () => {
        let ref = Firebase.database().ref("/");
        ref.on("value", snapshot => {
          const state = snapshot.val();
          setOnes(state)
        })
      }
    
      const handleAdd =() => {
        setOnes([...ones,1])
      }
        
        const list = ones.map((e,i)=>( 
            <div
                key={e.id}
            > {e} </div>
        ))
      
        return ( 
        <div>
          {list}
          <button onClick={handleAdd}>Add</button>
        </div> 
        )
    }
    
    export default App2