State returns null before data is fetched
Unanswered
Pond loach posted this in #help-forum
Pond loachOP
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const isLoggedIn = useLogStore((state) => state.isLoggedIn);
const router = useRouter();
const fetchUser = useCallback(async () => {
try {
if (isLoggedIn) {
const userData = await getUser();
setUser(userData);
} else {
setUser(null);
}
} catch (error) {
console.error("Failed to fetch user:", error);
} finally {
setLoading(false);
}
}, [isLoggedIn]);
useEffect(() => {
fetchUser();
}, [fetchUser]);
useEffect(() => {
if (!loading && !user) {
//router.push("/login");
}
}, [loading, user, router]);
if (loading) {
return <SpinnerLoading />;
}
console.log(user);const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const isLoggedIn = useLogStore((state) => state.isLoggedIn);
const router = useRouter();
const fetchUser = useCallback(async () => {
try {
if (isLoggedIn) {
const userData = await getUser();
setUser(userData);
} else {
setUser(null);
}
} catch (error) {
console.error("Failed to fetch user:", error);
} finally {
setLoading(false);
}
}, [isLoggedIn]);
useEffect(() => {
fetchUser();
}, [fetchUser]);
useEffect(() => {
if (!loading && !user) {
//router.push("/login");
}
}, [loading, user, router]);
if (loading) {
return <SpinnerLoading />;
}
console.log(user);The const isLoggedIn = useLogStore((state) => state.isLoggedIn); triggers a rerender.
console.log(user);
returns null the first time, but then it finds the user and returns it. Why is it returning null at first? I am logged in, and the isLoggedIn state is always true. Now since it returns null first, it runs the //router.push("/login"); which I dont want since I am actually logged in
74 Replies
its because you are using async function
user is null by default and until the useEffect executes your callback, it stays null
@Coffee Coke user is null by default and until the useEffect executes your callback, it stays null
Pond loachOP
So how would I make it so the
part runs only when the user is set?
Thanks for the quick answer btw
if (!loading && !user) {
//router.push("/login");
}part runs only when the user is set?
Thanks for the quick answer btw
Like only when the user is "loaded" if you know what I mean
@Coffee Coke try combining useEffects
Pond loachOP
So like fetching, and checking if its a user in one?
just want to see if it will work
oh, maybe its because of auth, I was recently working on the fireauth project, and it also took some time to find the user
I solved it by caching the user
or the data I needed to display
I think there is nothing you can do about that temporal null
Pond loachOP
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const router = useRouter();
const isLoggedIn = useLogStore((state) => state.isLoggedIn);
const fetchUser = useCallback(async () => {
try {
if (isLoggedIn) {
const userData = await getUser();
setUser(userData);
} else {
setUser(null);
}
} catch (error) {
console.error("Failed to fetch user:", error);
} finally {
setLoading(false);
}
}, [isLoggedIn]);
useEffect(() => {
fetchUser();
if (!loading && !user) {
//router.push("/login");
}
}, [fetchUser, loading, user, router]);
if (loading) {
return <SpinnerLoading />;
}
console.log(user);This works, but it spams requests
@Coffee Coke oh, maybe its because of auth, I was recently working on the fireauth project, and it also took some time to find the user
Pond loachOP
I use a custom auth system using JWT
@Coffee Coke I solved it by caching the user
Pond loachOP
How would I cache the user?
@Pond loach How would I cache the user?
use caching mechanism during fetching, there are several of them
@Coffee Coke use caching mechanism during fetching, there are several of them
Pond loachOP
Could you link me to the best one?
okay wait for a sec
Pond loachOP
Thanks
import React from 'react';
import useCachedData from './useCachedData'; // Adjust the import path as necessary
const MyComponent = () => {
const { data, isLoading, isError } = useCachedData({
url: 'https://example.com/api/data',
fetchData: async () => {
const response = await fetch('https://example.com/api/data');
if (!response.ok) throw new Error('Network response was not ok');
return await response.json();
},
});
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error occurred</div>;
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.title}</li>
))}
</ul>
);
};
;this is an example without state mamagments
same thing can be done using zustand or redux
this is AI generated example btw, to show you the logic
Pond loachOP
Is there any other way I can fix the issue I am having?
@Pond loach Is there any other way I can fix the issue I am having?
no, can't think of any
Masai Lion
kinda depends on how you're storing the jwt token, if its in a cookie, you can read the user in the server instead and do the loading with a suspense
@Masai Lion kinda depends on how you're storing the jwt token, if its in a cookie, you can read the user in the server instead and do the loading with a suspense
Pond loachOP
Thanks for the reply, I am storing it in a cookie. How would I read it on the server first?
Masai Lion
you can have it in a server component and directly fetch the user data without any hooks
import { cookies } from 'next/headers'; // Import the cookies module
export default async function MyPage() {
// Get the token from the cookie
const cookieStore = cookies();
const jwtToken = cookieStore.get('jwt_token');
// Fetch user data using the token
const user = await getUserData(jwtToken); // Your data-fetching function
// Render your component with the fetched data
return (
<div>
{/* Your component content */}
</div>
);
}@Masai Lion import { cookies } from 'next/headers'; // Import the cookies module
export default async function MyPage() {
// Get the token from the cookie
const cookieStore = cookies();
const jwtToken = cookieStore.get('jwt_token');
// Fetch user data using the token
const user = await getUserData(jwtToken); // Your data-fetching function
// Render your component with the fetched data
return (
<div>
{/* Your component content */}
</div>
);
}
Pond loachOP
How do people usually fetch data in a client component then?
Masai Lion
https://nextjs-forum.com/post/1246782748736356422#message-1246804969659830345 the way echo mentioned, with a hook and loading
@Masai Lion https://discord.com/channels/752553802359505017/1246782748736356422/1246804969659830345 the way echo mentioned, with a hook and loading
Pond loachOP
But like isnt caching what I am already doing by using a loading segment?
Masai Lion
yes, but you still need the loading for a cache miss
@Masai Lion yes, but you still need the loading for a cache miss
Pond loachOP
So why is it not working then?
Masai Lion
cache is just a bit of optimization, its not required
where is it not working
Echo already told you why your code was problematic, and gave you a solution to use caching and loading together
@Masai Lion Echo already told you why your code was problematic, and gave you a solution to use caching and loading together
Pond loachOP
But isnt that what I am already doing
Masai Lion
there is no "solution" to loading indicators, you need to load if you aren't getting data instantaneously
running it on the server reduces loading times so maybe you can get away with no loading indicators, and caching will have instantaneous results as long as theres no cache miss
@Masai Lion there is no "solution" to loading indicators, you need to load if you aren't getting data instantaneously
Pond loachOP
But once its not loading, is when its checking for the user, so the user should have been found, but it returns null instead
try {
const userData = await getUser();
setUser(userData);
} catch (error) {
console.error("Failed to fetch user:", error);
setUser(null);
} finally {
setLoading(false);
}The code should only continue once the loading is set to false, right?
Masai Lion
thats just how initial rendering works with useeffect, you start off with null, server components solve that since you fetch immediately
@Masai Lion thats just how initial rendering works with useeffect, you start off with null, server components solve that since you fetch immediately
Pond loachOP
But cant I have it so it starts with null, then fetches while the loading spinner is there, then wait for the fetch result. If the user is found, stay, else redirect
Masai Lion
thats already what you do
Pond loachOP
Yeah, is there a way I can start of with something else than null during the initial render?
Masai Lion
youre just doing it much more complicated than it needs to be
@Masai Lion youre just doing it much more complicated than it needs to be
Pond loachOP
Would you use server components?
Masai Lion
sure, just add a
|| whatever else you want, its just dummy data then, same as replacing your loading component with a skeletonPond loachOP
Its just irritating cuz in my head, this logic seems perfectly fine for my usage, but its just not working
@Masai Lion sure, just add a `|| whatever` else you want, its just dummy data then, same as replacing your loading component with a skeleton
Pond loachOP
well, wouldnt the value still be null, with the whatever part just being a backup?
Masai Lion
null || {user} = {user}, which is what you asked here isnt it?https://nextjs-forum.com/post/1246782748736356422#message-1246898199571075204@Masai Lion `null || {user} = {user}`, which is what you asked here isnt it?https://discord.com/channels/752553802359505017/1246782748736356422/1246898199571075204
Pond loachOP
Since it starts with null, and it seems to be running this part
even though its not supposed to since loading is set to true, wouldnt it solve the issue if we set the user to an empty object or something, then fetch, set it to null if it was not found, else set it to the user data, then finally do the check I mentioned above
useEffect(() => {
if (!loading) {
if (!user) {
return router.push("/login");
}
}
}, [loading, user, router]);even though its not supposed to since loading is set to true, wouldnt it solve the issue if we set the user to an empty object or something, then fetch, set it to null if it was not found, else set it to the user data, then finally do the check I mentioned above
Like as long as I am able to just make the fetch request, before checking if the user was found, its all good. Thats why I added the loading, and in my head, that should have fixed the porblem as the loading var is stopping the check from occouring during the first render, at least thats what I thought would happen, but clearly something else is happening
Masai Lion
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const router = useRouter();
useEffect(() => {
(async()=>{
try {
const userData = await getUser();
setUser(userData);
setLoading(false);
} catch (e) {
router.push("/login")
}
})()
}, []);
if (loading) {
return <SpinnerLoading />;
}
console.log(user);I'm not really sure what useLogStore is, it doesnt really seem necessary so I removed it
this is really all you need, if you want to redirect away if getUser failed
@Masai Lion const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const router = useRouter();
useEffect(() => {
(async()=>{
try {
const userData = await getUser();
setUser(userData);
setLoading(false);
} catch (e) {
router.push("/login")
}
})()
}, []);
if (loading) {
return <SpinnerLoading />;
}
console.log(user);
I'm not really sure what useLogStore is, it doesnt really seem necessary so I removed it
Pond loachOP
So I need the getUser() function to return an error if user was not found? Cuz now its returning null if it was not found
Masai Lion
getUser should throw an error
Pond loachOP
I see
Well, I will try. Thank you so much for the help!
Masai Lion
you should also read what error code it is to know why getUser failed, is it server issue, or login issue
@Masai Lion const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const router = useRouter();
useEffect(() => {
(async()=>{
try {
const userData = await getUser();
setUser(userData);
setLoading(false);
} catch (e) {
router.push("/login")
}
})()
}, []);
if (loading) {
return <SpinnerLoading />;
}
console.log(user);
I'm not really sure what useLogStore is, it doesnt really seem necessary so I removed it
Pond loachOP
I am guessing this probably isnt the best way to do it, but do you think it will work, or is it like its not even worth using the system I made, and I should just switch to something else
@Masai Lion you should also read what error code it is to know why getUser failed, is it server issue, or login issue
Pond loachOP
Yeah, I will do that
Masai Lion
I suggest using server components, its faster without the round trip data fetching, and cleaner
@Masai Lion I suggest using server components, its faster without the round trip data fetching, and cleaner
Pond loachOP
But do you think this will work for my use? Also, are there any downsides to using server components??
Masai Lion
you're using a basic data fetching pattern, it works very well for server components, especially since you already have a jwt stored in a cookie, theres no downsides since you can use client components inside server components
@Masai Lion you're using a basic data fetching pattern, it works very well for server components, especially since you already have a jwt stored in a cookie, theres no downsides since you can use client components inside server components
Pond loachOP
I see, but I remember reading somewhere that like its hard to edit data for server components, so like if you need to make changes to the data or something. I might be wrong
Masai Lion
if you wanted to edit stuff like in a useState, you would put that in a client component, so it doesnt really matter
Pond loachOP
Also, something completely unrelated, I want to make an economy system for the website, so users can earn coins for doing tasks. I need to display the amount of coins a user have on a navigation bar. Whats the best way to do live updates of the coin balance?
@Masai Lion if you wanted to edit stuff like in a useState, you would put that in a client component, so it doesnt really matter
Pond loachOP
Ok, I will look into it later, thanks
Masai Lion
https://nextjs-forum.com/post/1246900035434844312 basically this question
@Masai Lion https://discord.com/channels/752553802359505017/1246900035434844312 basically this question
Pond loachOP
OK, well thank you for the help