I have an application that gets data from an API. I'm trying to use conditional rendering so that if now data was not retrieved from the API display a message. The problem is that both of my states are set as empty arrays, and the condition I am using is arryname.length === 0. Every time my application renders the arrays initialize to a length of 0 so the message flashs the screen quickly then go away once the data is retrieved from the API. Any ideas on how to best solve this issue?
Thanks
import React, { useState, useEffect } from "react";
import {
NavBar,
Footer,
Home,
About,
Projects,
TextareaAutosize,
ToastContainer,
toast,
} from "./imports";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import { db } from "./firebase-config";
import {
collection,
doc,
updateDoc,
addDoc,
Timestamp,
query,
orderBy,
onSnapshot,
} from "firebase/firestore";
export default function App() {
const [formData, setFormData] = useState({ name: "", comment: "" });
const [numberOfVisitors, setnumberOfVistors] = useState([]);
const [userComments, setUserComments] = useState([]);
const userCommentsRef = collection(db, "user-comments");
const addNewComment = async (event) => {
event.preventDefault();
if (formData.name === "") {
const nameFieldErrorMessage = toast.error(" Error: Please Enter A Name", {
position: "top-right",
hideProgressBar: true,
autoClose: false,
});
return nameFieldErrorMessage;
}
if (formData.comment === "") {
const commentFieldErrorMessage = toast.error(
" Error: Please Enter A Comment",
{
position: "top-right",
hideProgressBar: true,
autoClose: false,
}
);
return commentFieldErrorMessage;
}
const commentSuccessMessage = toast.success(" Comment Posted!", {
position: "top-right",
hideProgressBar: true,
autoClose: false,
});
const newComment = {
name: formData.name,
comment: formData.comment,
date: Timestamp.now(),
};
setFormData({ name: "", comment: "" });
try {
await addDoc(userCommentsRef, newComment);
return commentSuccessMessage;
} catch (err) {
console.log(err);
}
};
const handleFormData = (event) => {
setFormData((prevFormData) => {
return {
...prevFormData,
[event.target.name]: event.target.value,
};
});
};
useEffect(() => {
const portfolioStatsRef = collection(db, "portfolio-stats");
const q = query(portfolioStatsRef);
const unsubscribeFromEventListener = onSnapshot(q, (snapshot) => {
const VisitorCountFromDB = snapshot.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}));
setnumberOfVistors(VisitorCountFromDB);
});
return () => unsubscribeFromEventListener();
}, []);
useEffect(() => {
const updateVisitorCount = async () => {
try {
const portfolioStatsDoc = doc(
db,
"portfolio-stats",
numberOfVisitors[0].id
);
const updatedFields = {
visitor_count: numberOfVisitors[0].visitor_count 1,
};
await updateDoc(portfolioStatsDoc, updatedFields);
} catch (err) {
console.log(err " at updateVisitorCount function");
}
};
if (!numberOfVisitors.length) return;
let sessionKey = sessionStorage.getItem("sessionKey");
if (sessionKey === null) {
sessionStorage.setItem("sessionKey", "randomString");
updateVisitorCount();
}
}, [numberOfVisitors]);
useEffect(() => {
const q = query(userCommentsRef, orderBy("date", "asc"));
onSnapshot(q, (snapshot) => {
const userCommentsFromDB = snapshot.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}));
setUserComments(userCommentsFromDB);
});
}, [userCommentsRef]);
const currentNumberOfVisitors = numberOfVisitors.map((visitors) => {
return (
<h2 className="p-3 mb-0 bg-dark bg-gradient text-white" key={visitors.id}>
Number of vistors: {visitors.visitor_count}
</h2>
);
});
const listOfUserComments = userComments.map((comment) => {
return (
<li className="list-group-item" key={comment.id}>
<div className="d-flex w-100 justify-content-between">
<h5 className="ms-1">{comment.name}</h5>
<small className="fw-bold">
{comment.date.toDate().toLocaleString()}
</small>
</div>
<p className="mb-1">{comment.comment}</p>
</li>
);
});
return (
<>
<div className="d-flex flex-column overflow-hidden min-vh-100 vh-100">
<NavBar />
<div className="row">
<div className="col text-center">
{currentNumberOfVisitors.length === 0 && (
<h2 className="p-3 mb-0 bg-dark bg-gradient text-danger">
Sorry, the Firestore free tier quota has been met for today.
Please come back tomorrow to see portfilio stats.
</h2>
)}
{currentNumberOfVisitors}
</div>
</div>
<div className="bg-image">
<div className="postion-relative">
<main className="flex-grow-1">
<div className="container-fluid p-0">
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/projects" element={<Projects />} />
</Routes>
<div className="row">
<div className="center-items col">
<h2 className="text-light fw-bold mb-1">Comments</h2>
</div>
</div>
<div className="row">
<div className="center-items col">
<div className="comments-container">
{userComments.length === 0 && (
<h4 className="text-danger bg-dark m-1 p-1">
Sorry, the Firestore free tier quota has been met
for today. Please come back tomorrow to see
portfilio comments.
</h4>
)}
{listOfUserComments}
</div>
</div>
</div>
<div className="row">
<div className="center-items col">
<h4 className="text-light fw-bold">Leave a comment</h4>
</div>
</div>
<div className="row">
<div className="center-items col">
<form className="comment-form">
<div className="form-floating mb-3">
<input
type="text"
className=" form-control bg-transparent "
id="floatingInput"
name="name"
onChange={handleFormData}
value={formData.name}
/>
<label htmlFor="floatingInput">Name</label>
</div>
<div className="form-floating">
<TextareaAutosize
className="form-control form-textarea-field bg-transparent mb-1"
name="comment"
id="floatingTextarea"
minRows={4}
onChange={handleFormData}
value={formData.comment}
/>
<label htmlFor="floatingTextarea">Comment</label>
</div>
<div className="d-grid">
<button
className="btn btn-primary mb-4"
onClick={addNewComment}
>
Add Comment
</button>
</div>
</form>
</div>
</div>
</Router>
</div>
</main>
</div>
</div>
<Footer />
</div>
<ToastContainer />
</>
);
}
CodePudding user response:
Usually, you use a boolean flag to signal if your API call is still in progress. If it is, don't display the error.
const [isLoading, setIsLoading] = useState(true);
Set this flag to false
when your API call has completed:
setIsLoading(false);
Then in your rendering logic:
{(!isLoading && currentNumberOfVisitors.length === 0) && (
<h2 className="p-3 mb-0 bg-dark bg-gradient text-danger">
Sorry, the Firestore free tier quota has been met for today.
Please come back tomorrow to see portfilio stats.
</h2>
)}