skip to Main Content

I have an async function (getDataFromDB) which I run inside of an useEffect. The function gets data from firestore. The problem is I can’t set state to data from firebase. I get the reference to data, I can see the data in the console by logging it, but the setConfigs doesn’t work, the configs state remains undefined.

// App

// imports
import { db } from "./firebase-config";
import {
  query,
  doc,
  collection,
  onSnapshot,
  getDocs,
  getDoc,
  Firestore,
} from "firebase/firestore";
// from packages
import { useState, useEffect, useRef, useCallback } from "react";
import { Outlet } from "react-router-dom";
// sections
import Navbar from "./components/sections/Navbar";
import SearchBar from "./components/sections/Search-bar";
import TrendingBar from "./components/sections/Trending-bar";
// context
import ContextProviders from "./context-config";
// utils
import {
  getGenres,
  getTrendingData,
  getMovies,
  getTv,
  onStartIntoDB,
  getDataFromDB,
} from "./utils/fetchData";
// global constants
import { MAP_URL } from "./data/global-constants";
import { FirebaseError } from "firebase/app";

export default function App() {
  // States
  // genres and configs
  const [genres, setGenres] = useState([]);
  
  const [loading, setLoading] = useState("true");
  
  // ==============================
  // HERE IS CONFIGS STATE DECLARED
  // ==============================
  const [configs, setConfigs] = useState([]);

  // movies data
  const [trendingData, setTrendingData] = useState([]);
  const [moviesData, setMoviesData] = useState([]);
  const [popularMoviesData, setPopularMoviesData] = useState([]);
  const [topRatedMoviesData, setTopRatedMoviesData] = useState([]);
  const [upcomingMoviesData, setUpcomingMoviesData] = useState([]);
  const [nowPlayingMoviesData, setNowPlayingMoviesData] = useState([]);
  // tv data
  const [tvData, setTvData] = useState([]);
  const [airingTodayTvData, setAiringTodayTvData] = useState([]);
  const [onTheAirTvData, setOnTheAirTvData] = useState([]);
  const [popularTvData, setPopularTvData] = useState([]);
  const [topRatedTvData, setTopRatedTvData] = useState([]);

  // Refs
  const preventEffect = useRef(true);

  // ---------------------------------------
  // Fetch data from api and push it into db
  // ---------------------------------------

  // configs
  useEffect(() => {
    onStartIntoDB(
      preventEffect,
      "getConfigs",
      MAP_URL.configuration.base_url,
      null,
      null,
      process.env.REACT_APP_API_KEY,
      null,
      "relating-data",
      "configs"
    );
  }, []);

  // genres for movies
  useEffect(() => {
    onStartIntoDB(
      preventEffect,
      "getGenres",
      MAP_URL.genres.base_url,
      MAP_URL.genres.media_type.movie,
      null,
      process.env.REACT_APP_API_KEY,
      null,
      "relating-data",
      "genres-movie"
    );
  }, []);

  // genres for tv
  useEffect(() => {
    onStartIntoDB(
      preventEffect,
      "getGenres",
      MAP_URL.genres.base_url,
      MAP_URL.genres.media_type.tv,
      null,
      process.env.REACT_APP_API_KEY,
      null,
      "relating-data",
      "genres-tv"
    );
  }, []);

  // trending movies
  useEffect(() => {
    onStartIntoDB(
      preventEffect,
      "getTrendingData",
      MAP_URL.trendingMovies.base_url,
      MAP_URL.trendingMovies.media_type.all,
      MAP_URL.trendingMovies.time_window.week,
      process.env.REACT_APP_API_KEY,
      null,
      "media",
      "trending-movies"
    );
  }, []);

  // movies
  useEffect(() => {
    onStartIntoDB(
      preventEffect,
      "getMovies",
      MAP_URL.movies.base_url,
      null,
      null,
      process.env.REACT_APP_API_KEY,
      MAP_URL.movies.lang_and_page,
      "media",
      "movies"
    );
  }, []);

  // tv
  useEffect(() => {
    onStartIntoDB(
      preventEffect,
      "getTv",
      MAP_URL.tv.base_url,
      null,
      null,
      process.env.REACT_APP_API_KEY,
      MAP_URL.tv.lang_and_page,
      "media",
      "tv"
    );
  }, []);
   
   // =================================================================
   // Right below is the useEffect that should update the state with an
   // async funtion. 
   // =================================================================   
useEffect(() => {
   async function getDataFromDB() {
      const docRef = doc(db, "relating-data", "configs");
      console.log(docRef); // => ya {converter: null, _key: ct, type: 
                           //   'document', firestore: ih}

      const docSnap = await getDoc(docRef);
      console.log(docSnap); // => ya {converter: null, _key: ct, type: 
                            //    'document', firestore: ih}

      const dataArr = []; 

      dataArr.push(docSnap.data());
      console.log(dataArr); // => [{…}] (and inside the object is the data: 
                            //    {change_keys: Array(53), images: {…}} )
 
      setConfigs((prevData) => {
        console.log("setConfigs executed!"); // => NOT EXECUTED!!!
        const nextData = {
          ...prevData,
          ...dataArr,
        };
        return nextData;
      });
    }
    getDataFromDB();
  }, []);

  // ==================================
  console.log(configs); // => undefined
  // ==================================

  // genres
  useEffect(() => {
    async function g() {
      const data = await getGenres(
        MAP_URL.genres.base_url,
        MAP_URL.genres.media_type.movie,
        process.env.REACT_APP_API_KEY
      );
      setGenres(data);
    }
    g();
  }, []);

  // trending movies
  useEffect(() => {
    return async function () {
      const data = await getTrendingData(
        MAP_URL.trendingMovies.base_url,
        MAP_URL.trendingMovies.media_type.all,
        MAP_URL.trendingMovies.time_window.week,
        process.env.REACT_APP_API_KEY
      );
      setTrendingData(data.results);
    };
  }, []);
  // movies
  useEffect(() => {
    return async function () {
      const data = await getMovies(
        MAP_URL.movies.base_url,
        process.env.REACT_APP_API_KEY,
        MAP_URL.movies.lang_and_page
      );
      setMoviesData(data);
    };
  }, []);

  // tv
  useEffect(() => {
    return async function () {
      const data = await getTv(
        MAP_URL.tv.base_url,
        process.env.REACT_APP_API_KEY,
        MAP_URL.tv.lang_and_page
      );
      setTvData(data);
    };
  }, []);

  // ----------------------------------
  // set popular movies
  useEffect(() => {
    setPopularMoviesData(moviesData[0]);
  }, [moviesData[0]]);
  // set top-rated movies
  useEffect(() => {
    setTopRatedMoviesData(moviesData[1]);
  }, [moviesData[1]]);
  // set now-playing movies
  useEffect(() => {
    setNowPlayingMoviesData(moviesData[2]);
  }, [moviesData[2]]);
  // set upcoming movies
  useEffect(() => {
    setUpcomingMoviesData(moviesData[3]);
  }, [moviesData[3]]);
  // ----------------------------------
  // set airing-today tv
  useEffect(() => {
    setAiringTodayTvData(tvData[2]);
  }, [tvData[2]]);
  // set on-the-air tv
  useEffect(() => {
    setOnTheAirTvData(tvData[3]);
  }, [tvData[2]]);
  // set popular tv
  useEffect(() => {
    setPopularTvData(tvData[0]);
  }, [tvData[0]]);
  // set top-rated tv
  useEffect(() => {
    setTopRatedTvData(tvData[1]);
  }, [tvData[1]]);
  // ----------------------------------

  return (
    <ContextProviders configs={configs} trendingData={trendingData}>
      <div className="App">
        <Navbar />
        <SearchBar />
        <TrendingBar />
        <Outlet />
      </div>
    </ContextProviders>
  );
}

I tried to set state from outside of the async function, but in this case I have call another async, so I don’t really understand why it doesn’t set state. The same operation with fetching data from api’s works, for example if using fetch().then().then().catch(). I tried .then() chain here as well but without any results. I read a lot of articles on google, but it doesn’t work for my case, maybe there is something inside my entire code? So my question is how to correctly set state when getting data from a firestore?

2

Answers


  1. Chosen as BEST ANSWER

    The problem was - The set state worked, but for some reasons, there wasn't triggered another re-render and it ended up just like it wasn't assigned. Then I consumed the state in order to trigger another render, and it works. So my lesson from this case is to keep in mind that useState is async, make sure to re-render in order to see the state.

    Here's the solution that worked for me:

    export async function getDataFromDB(setState, collection_name) {
      await getDocs(collection(db, collection_name)).then((querySnapshot) => {
        const newData = querySnapshot.docs.map((doc) => ({
          ...doc.data(),
          id: doc.id,
        }));
        setState(newData);
      });
    }


  2. The current implementation is not explicitly returning a value so the next state value will be undefined. You need to return the next computed state value.

    setConfigs((prevData) => {
      console.log("Running setConfigs!!!!!!!!!!!!!!!!!");
    
      const nextData = {
        ...prevData,
        ...dataArr,
      };
    
      return nextData; // <-- return next state value
    });
    

    More succinctly:

    setConfigs((prevData) => ({
      ...prevData,
      ...dataArr,
    }));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search