I have a date range filter in which when submitted, it will update new values of dateStart and dateEnd using setDateStart and setDateEnd then it will pass down the new values to my useCollection custom hook. Why is it that when it rerenders, the useCollection custom hook arguments doesn't get the updated values?
*the custom useCollection hook is used for data Fetching on Firebase *the dateStart and dateEnd is used for filtering the data displayed on the screen
useCollection custom hook:
import { useEffect, useState, useRef } from "react";
import { projectFirestore } from "../firebase/config";
export const useCollection = (
collection,
_query,
_query2,
_query3,
_orderBy
) => {
const [documents, setDocuments] = useState(null);
const [error, setError] = useState(null);
// if we don't use a ref --> infinite loop in useEffect
// _query is an array and is "different" on every function call
const query = useRef(_query).current;
const query2 = useRef(_query2).current;
const query3 = useRef(_query3).current;
const orderBy = useRef(_orderBy).current;
console.log("from query: " + query);
console.log("from query2: " + query2);
console.log("from query2: " + query3);
useEffect(() => {
let ref = projectFirestore.collection(collection);
if (query) {
ref = ref.where(...query);
}
if (query2) {
ref = ref.where(...query2);
}
if (query3) {
ref = ref.where(...query3);
}
if (orderBy) {
ref = ref.orderBy(...orderBy);
}
const unsubscribe = ref.onSnapshot(
(snapshot) => {
let results = [];
snapshot.docs.forEach((doc) => {
results.push({ ...doc.data(), id: doc.id });
});
// update state
setDocuments(results);
setError(null);
},
(error) => {
console.log(error);
setError("could not fetch the data");
}
);
// unsubscribe on unmount
return () => unsubscribe();
}, [collection, query, query2, query3, orderBy]);
return { documents, error };
};
TimeCard.js
import React, { useState, useEffect } from "react";
import moment from "moment";
import { useAuthContext } from "../../hooks/useAuthContext";
import { useCollection } from "../../hooks/useCollection";
import Table from "../../components/Table";
import DateRange from "../../components/DateRange";
import ErrorMessage from "../../components/ErrorMessage";
const TimeCard = () => {
const { uid } = useAuthContext().user;
let m1 = moment(new Date());
let m2 = moment();
m1.startOf("month").startOf("day");
m2.endOf("day");
const [dateStart, setDateStart] = useState(m1.toDate());
const [dateEnd, setDateEnd] = useState(m2.toDate());
console.log("Moment1 from State: ", dateStart);
console.log("Moment2: from State", dateEnd);
const [time1, setTime1] = useState("");
const [time2, setTime2] = useState("");
const [error, setError] = useState("");
console.log("RENDER");
const { documents } = useCollection(
"timeinout",
["uid", "==", uid],
["createdAt", ">=", dateStart],
["createdAt", "<=", dateEnd],
["createdAt", "asc"]
);
const handleSubmit = (e) => {
e.preventDefault();
var d1 = new Date(time1);
var d2 = new Date(time2);
if (!time1 || !time2) {
setError(
"Either ONE or BOTH of the date inputs below are empty, please select a date!"
);
return null;
}
if (d1.getTime() > d2.getTime()) {
setError(
"Invalid Date: Date 2 (To:) Input is greater than Date 1 (From:) Input"
);
return null;
}
const s1 = moment(time1).startOf("day").toDate();
const s2 = moment(time2).endOf("day").toDate();
setDateStart(s1);
setDateEnd(s2);
};
useEffect(() => {
const time = setTimeout(() => {
setError("");
}, [5000]);
return () => {
clearTimeout(time);
};
}, [error]);
return (
<>
<div className="flex flex-col">
{error && <ErrorMessage msg={error} />}
<DateRange
time1={time1}
time2={time2}
setTime1={setTime1}
setTime2={setTime2}
handleSubmit={handleSubmit}
/>
</div>
{documents && <Table dataFromDatabase={documents} />}
</>
);
};
export default TimeCard;
Implemented it without using a custom hook instead, basically all the codes from the useCollection custom hook were transferred to TimeCard.js and created a normal useEffect hook that process all the changes in the filter for the dates.
See code below:
import React, { useState, useEffect } from "react";
import { projectFirestore } from "../../firebase/config";
import moment from "moment";
import { useAuthContext } from "../../hooks/useAuthContext";
import Table from "../../components/Table";
import DateRange from "../../components/DateRange";
import ErrorMessage from "../../components/ErrorMessage";
const TimeCard = () => {
const { uid } = useAuthContext().user;
const [error, setError] = useState("");
const [documents, setDocuments] = useState(null);
const startOfMonth = moment().startOf("month").format("YYYY-MM-DD");
const endOfMonth = moment().endOf("month").format("YYYY-MM-DD");
console.log("startOfMonth: ", startOfMonth);
console.log("endOfMonth: ", endOfMonth);
const [time1, setTime1] = useState(startOfMonth);
const [time2, setTime2] = useState(endOfMonth);
const [query] = useState(["uid", "==", uid]);
const [query2, setQuery2] = useState([
"createdAt",
">=",
moment(time1).startOf("day").toDate(),
]);
const [query3, setQuery3] = useState([
"createdAt",
"<=",
moment(time2).endOf("day").toDate(),
]);
const [orderBy] = useState(["createdAt", "asc"]);
const handleSubmit = (e) => {
e.preventDefault();
var d1 = new Date(time1);
var d2 = new Date(time2);
if (!time1 || !time2) {
setError(
"Either ONE or BOTH of the date inputs below are empty, please select a date!"
);
return null;
}
// Loseless code below because of > comparison operator
if (d1.getTime() > d2.getTime()) {
setError(
"Invalid Date: Date 2 (To:) Input is greater than Date 1 (From:) Input"
);
return null;
}
setQuery2(["createdAt", ">=", moment(time1).startOf("day").toDate()]);
setQuery3(["createdAt", "<=", moment(time2).endOf("day").toDate()]);
};
useEffect(() => {
const time = setTimeout(() => {
setError("");
}, [5000]);
return () => {
clearTimeout(time);
};
}, [error]);
useEffect(() => {
let ref = projectFirestore.collection("timeinout");
if (query) {
ref = ref.where(...query);
}
if (query2) {
ref = ref.where(...query2);
}
if (query3) {
ref = ref.where(...query3);
}
if (orderBy) {
ref = ref.orderBy(...orderBy);
}
const unsubscribe = ref.onSnapshot(
(snapshot) => {
let results = [];
snapshot.docs.forEach((doc) => {
results.push({ ...doc.data(), id: doc.id });
});
// update state
setDocuments(results);
setError(null);
},
(error) => {
console.log(error);
setError("could not fetch the data");
}
);
// unsubscribe on unmount
return () => unsubscribe();
}, [query, query2, query3, orderBy]);
console.log("Updated documents OUTSIDE: ", documents);
return (
<>
<div className="flex flex-col">
{error && <ErrorMessage msg={error} />}
<DateRange
time1={time1}
time2={time2}
setTime1={setTime1}
setTime2={setTime2}
handleSubmit={handleSubmit}
/>
</div>
{documents && <Table documents={documents} />}
</>
);
};
export default TimeCard;