Home > Blockchain >  useState not updating rendered values after getting document snapshots
useState not updating rendered values after getting document snapshots

Time:10-23

I am having a problem assigning data to useState by fetching data using reference type value from firebase.

const [preOil, setPreOil] = useState([]);
const [oilChange, setOilChange] = useState([]);

useEffect(() => {
getDocs(query(collection(db, "oil"), orderBy("timestamp"))).then(
  (snapshot) => {
    setPreOil(
      snapshot.docs.map((doc) => ({
        id: doc.id,
        ...doc.data(),
      }))
    );
  }
);
}, []);


useEffect(() => {
  let current = preOil.length > 0 ? [...preOil] : [];

  current.map((_oil, i) => {
    getDoc(_oil.oilReference).then((oilRef) => {
      current[i].oilReference = oilRef.data();
    });
  });

  setOilChange(current);
}, [preOil]);

In the first useEffect, the data is fetched successfully in this form,

preOil = {
        id:"RxbAOAIs8d3kGOHNskJ4",
        oilReference: Ta {converter: null, _key: ut, type: 'document', firestore: Na},
        customerName: "USAMA",
}

In the second useEffect based on [preOil], I need to reassign the oilReference with the data fetched from firestorm through its reference(oilReference), The data is successfully fetched from the database and assigned to current but The main problem is when I set to state setOilChange(current) it updates my oilChange state when I inspect in inspect tools in chrome but in JSX the changes don't reflect

  • I am updating the state in useEffect
  • I am having desired data assigned in a local variable and assign that variable to the state

Then What am I missing?

CodePudding user response:

In your second useEffect(), more specifically, in

current.map((_oil, i) => {
  getDoc(_oil.oilReference).then((oilRef) => {
    current[i].oilReference = oilRef.data();
  });
});
setOilChange(current);

You are mutating the content of the current variable. This mutation, because it is async, will happen after the setOilChange call. Such mutation will thus not trigger a re-render.

What you need is to instead first wait for all the docs to be loaded and only after that set the state. Example:

const _docs = current.map((_oil, i) => {
  return getDoc(_oil.oilReference).then((oilRef) => { // changed here
    return {                                          // changed here
      ...current[i],                                  // changed here
      oilReference: oilRef.data()                     // changed here
    }                                                 // changed here
  });                                                 // changed here
});

Promise.all(_docs).then(() => {
  setOilChange(_docs);
});

Also notice I didn't mutate current, rather I returned a new object. This is not mandatory but just a best practice overall.

  • Related