wch
wch

Reputation: 304

How to render data from Firestore on a page React

In my application, for some reason, data for the Home page is loaded only after going to another page and then returning to the Home page.

This is most likely due to the fact that the data from the firestore is rendered for several seconds, and the data is transmitted to the home page as an empty array.

I understand that this is something like this, but how to solve this problem I can not find a solution.

Also, if you see any obvious errors in my code, please tell me, since I just started learning React and can still make some stupid mistakes

Menu

function Menu() {
  const [data, setData] = useState([]);
  const { user } = UserAuth();
  const fetchProduct = async () => {
      const querySnapshot = await getDocs(collection(db, "langcards-db"));
      const arr = [];
      querySnapshot.forEach((doc) => {
        arr.push({
          ...doc.data(),
          id: doc.id,
        });
      });
      setData(arr);
  }
  useEffect(() => {
    if(user){
      fetchProduct();
    }else{
      setData([])
    }
  }, []);
  useEffect(() => {
    if(user){
      fetchProduct();
    }else{
      setData([])
    }  
  }, [user]);

 
  return (
    <div className="content">
      <Routes>
        <Route
          path="/"
          element={<Layout onSearch={() => onSearchF()} data={data} />}
        >
          <Route index element={<Home  data={data} num={data.length} />}/> 
          <Route
            path="saved"
            element={<Saved data={data} setData={setData} />}
          />            
        </Route>
      </Routes>
    </div>
  );
}
export default Menu;

Home

const shuffle = (arr) => arr.sort(() => Math.random() - 0.5);
function Home({ data, num }) {
  const [newArr, setNewArr] = useState(shuffle([...data]));
  const [i, setI] = useState(0);

  const randomCard = useMemo(() => {
    if (!newArr || i < 0 || i >= newArr.length) return undefined;
    return newArr[i];
  }, [newArr, i]);

  const randomizeCard = () => {
    if (i < newArr.length - 1) {
      setI(i + 1);
    } else {
      setNewArr(shuffle([...data]));
      setI(0);
    }
  };

  return (
    <div>
      <div className="home-inner">
        {num >= 1 && randomCard ? (
          <div className="home-stat">
            <p className='saved-stat'>
              Saved words: <span>{num}</span>
            </p>
            <div onClick={randomizeCard} className="home-random">
              <div className="hr-card">
                <p className="randomword">{randomCard.word}</p>
                <p className="randomtranslate">{randomCard.translate}</p>
              </div>
            </div>
            <Link className="home-btn main-btn" to="/saved">
              See all
            </Link>
          </div>
        ) : (
          <div>
            {" "}
            <div className="oops-section">
            <Link className="main-btn" to="/addcard">
              Add first word
            </Link>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

Upvotes: 0

Views: 42

Answers (1)

kingkong.js
kingkong.js

Reputation: 753

You can use a state to check when your fetch is finished and show your data only then. See below

function Menu() {
  const [data, setData] = useState([]);
  const { user } = UserAuth();
  const [isLoading, setIsLoading] = useState(true); // Added state
  const fetchProduct = async () => {
      const querySnapshot = await getDocs(collection(db, "langcards-db"));
      const arr = [];
      querySnapshot.forEach((doc) => {
        arr.push({
          ...doc.data(),
          id: doc.id,
        });
      });
      setData(arr);
      setLoading(false);
  }
 /* useEffect(() => {   ---> this code this redundant and can be removed
    if(user){
      fetchProduct();
    }else{
      setData([])
    }
  }, []);*/
  useEffect(() => {
    if(user){
      fetchProduct();
    }else{
      setData([])
    }  
  }, [user]);
  
  
  const HomeRoute = () => isLoading ? <div>Loading...</div> : <Home  data={data} num={data.length} /> // Conditional rendering

 
  return (
    <div className="content">
      <Routes>
        <Route
          path="/"
          element={<Layout onSearch={() => onSearchF()} data={data} />}
        >
          <Route index element={<HomeRoute/>}/> 
          <Route
            path="saved"
            element={<Saved data={data} setData={setData} />}
          />            
        </Route>
      </Routes>
    </div>
  );
}
export default Menu;

Upvotes: 1

Related Questions