0

I am working to a project using ReactNative and FireStore.

I have to render data retrieved from the server.

I do not care about update of the data in short period, I have to retrieve data only one time.

This is the general scenario.

More specifically I want to store fetched data in a state. The render is based on this state. the state, after fetching, should be something like:

list = [{category:"ct1",items:[obj1,obj2,obj3,...]},{category:"ct2",items:[obj1,obj3]}]

If I use the following code Nothing is rendered, in details I have 2 problems.

  1. the console.log(list) show me that list (the state) is empty, but console.log(results) works.
  2. after a while the console show this error: Rendered more hooks than during the previous render.
const [list, setList] = useState([]);

function getData() {
    const final = []
    const categories = ["ct1", "ct2", "ct3", "ct4"]
    var promises = categories.map((category) => {
        return db.collection('items')
            .where("category", "array-   contains", category)
            .get()
            .then((querySnapshot) => {
                const items = [];
                querySnapshot.forEach((doc) => {
                    items.push(doc.data())
                });
                final.push({
                    category: category,
                    items: items
                })
            })
    })
    Promise.all(promises).then(function(results) {
        console.log("final", final)
        //this print [{category:"ct1",items:[obj1,obj2,obj3,...]},{categor
        setList(final)
        //so I have the final array, I can set the state
        console.log("list", list)
        //when I call this list is empty

    })
};

useEffect(() => {
    getData()
}, []);

return ({
    list.map((item, index) => (
        ......
        //this function iterate over the list, is very long and works.
        //I have tested on a predefined state. So the problem is not this.
    ))
})
Andrew
  • 795
  • 4
  • 18
  • 1
    Formatting the code, with sensible indentation would make it easier for someone to help you understand the issue. – Pete Dec 01 '20 at 09:36
  • 1
    Your code looks fine. I don't think the "*Rendered more hooks than during the previous render.*" error comes from the part that you posted. – Bergi Dec 01 '20 at 11:11
  • It might be a good idea to have a look at this excellent answer to [managing an asynchronous call](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call)? the bit about Promises with [async and await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) is of interest here. It might explain why your state is empty. Perhaps restructuring your code would be a better option. – Andrew Dec 02 '20 at 12:00

1 Answers1

0

Try to fill the final array when Promise.all is resolved:

function getData() {
    const final = [];
    const categories = ["ct1", "ct2", "ct3", "ct4"];
    var promises = categories.map((category) => {
      return db
        .collection("items")
        .where("category", "array-   contains", category)
        .get();
    });
    Promise.all(promises).then(function (results) {
      results.map((querySnapshot) => {
        const items = [];
        querySnapshot.forEach((doc) => {
          items.push(doc.data());
        });
        final.push({ category: category, items: items });
      });
      console.log("final", final); //this print [{category:"ct1",items:[obj1,obj2,obj3,...]},{categor
      setList(final); //so I have the final array, I can set the state
    });
  }

  useEffect(() => {
    getData();
  }, []);
lissettdm
  • 12,267
  • 1
  • 18
  • 39