So, I am wondering if this makes sense at all. I am trying to cache the data/users when the data is fetched with Axios, but make this useEffect run only when cachedUsers.length has no length. Does this have any propose or is there a better way to do this?
import { useState, useEffect, useMemo } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
const GetAllUsers = () => {
const [fsUsers, setFsUsers] = useState([]);
const [notFoundErr, setNotFoundErr] = useState("");
const [loading, toggleLoading] = useState(true);
const navigate = useNavigate();
const cachedUsers = useMemo(() => fsUsers, [fsUsers]);
// Gets all users in Firestore DB.
useEffect(() => {
const fetchUsers = async () => {
try {
toggleLoading(true);
const res = await axios({
method: "GET",
url: "http://localhost:4000/auth/api/firebase/users",
validateStatus: (status) => {
return status === 200 || status === 404;
},
});
console.log(res.data);
if (res && res.status === 200) {
setFsUsers(res.data);
} else if (res && res.status === 404) {
setNotFoundErr("No users found.");
}
} catch (error) {
console.error(error);
navigate("/error500");
} finally {
toggleLoading(false);
}
};
fetchUsers();
}, [!cachedUsers.length]);
return [cachedUsers, notFoundErr, loading];
};
export default GetAllUsers;
3
Answers
This is what I was trying to aim for. Cache the user (for profile page) when the user visits that page for the first time. Then when the user navigates to that page again, it doesn't have to send that request to the server and not waste time and resources.
The
useMemo
in your code has no effect since you’re just returning the value in state (you can use that directly).useMemo
will re-evaluate every timefsUsers
updates so unless you were transforming/normalizing that data there’s no point to memoize.In terms of the
useEffect
if I understand correctly, you want to make this request once, so you can just use an empty dependency array to make the api call whenever the hook mounts:!cachedUsers.length
istrue
if and only ifcachedUsers.length == 0
.Including
!cachedUsers.length
in the useEffect dependency array will trigger the hook to run whenever!cachedUsers.length
changes,this only happens when
!cachedUsers.length
goes fromtrue
tofalse
or the other way around.In other words when
cachedUsers.length
goes from0
ton
or the other way around..So this useEffect runs :
on mount (this will happen anyway) so if that’s enough and you want your useEffect to fire only once, after the component is mounted, then give it an empty dependency array
[]
as second parameter.when cachedUsers become an empty array (but not if it was already an empty array).
also when the cachedUsers is no longer an empty array: for example after the first mount, useEffect runs and updates
fsUsers
which will trigger useMemo in the next render and this latter will trigger useEffect becausecachedUsers
is no longer empty array).to avoid this, you can add a condition inside the useEffect callback function:
this won’t prevent useEffect from running but at least the code inside it won’t be executed if
cachedUsers
is not empty, in other words whencachedUsers.length
goes from0
ton
since you don’t care about this caseHere is a codesandbox without strict mode, have a look, and try comment/uncomment the commented code
keep in mind that:
As it is mentioned in @DannyMoshe answer the useMemo hook in your example is useless, its value changes each time
fsUsers
is updated so it is nothing but a copy of it.get rid of it and use
fsUsers
instead