Is global state necessary? How to replace it?
Unanswered
American Shorthair posted this in #help-forum
American ShorthairOP
Hello,
In past projects, I used global state, but over time it hasn’t worked well with Next.js. I sometimes encounter hydration errors, and I'm starting to wonder—do I even need global state? Wouldn't it be better to use query parameters or other ways instead?
I have some questions about how to handle these common situations without using a global store:
1) Tokens
When a user logs in or registers, I receive a token from the server (external backend). I used to store this token in the global store and pass it along with client-side API calls. It also helped determine whether a user was authenticated in various components.
Now my question is: How can I manage this without global state? How do I know if the user is authenticated?
2) User Data
After authentication, I receive user data (e.g., avatar and username) from the server. I used to store this data in the global store and access it in different components.
If I store it using useState, the data will be lost on navigation.
If I call the user data endpoint in each component that needs it, I'll end up with multiple duplicate API calls.
What's the best way to handle this scenario now?
In past projects, I used global state, but over time it hasn’t worked well with Next.js. I sometimes encounter hydration errors, and I'm starting to wonder—do I even need global state? Wouldn't it be better to use query parameters or other ways instead?
I have some questions about how to handle these common situations without using a global store:
1) Tokens
When a user logs in or registers, I receive a token from the server (external backend). I used to store this token in the global store and pass it along with client-side API calls. It also helped determine whether a user was authenticated in various components.
Now my question is: How can I manage this without global state? How do I know if the user is authenticated?
2) User Data
After authentication, I receive user data (e.g., avatar and username) from the server. I used to store this data in the global store and access it in different components.
If I store it using useState, the data will be lost on navigation.
If I call the user data endpoint in each component that needs it, I'll end up with multiple duplicate API calls.
What's the best way to handle this scenario now?
23 Replies
American ShorthairOP
anyone?
Roseate Spoonbill
There are multiple ways to adrres this. Personally, I go with localStorage and useLocalStorage if tokens need to be stored in browser and should live accross sessions. If I don't need localStorage persistance, or need more features to manage the state, I go with state store libraries.
zustand
, or xstate store
or other similar libraries to use and manipulate shared states. I'd steer away from react contexts for global states - contexts work much better in smaller component sets (e.g. Form
component creates context, and then Form.Input
consumes it), but on global level they cause issues with rerenders.Roseate Spoonbill
Bear in mind though, global store will be accessible only on client side if implemented the way I mentioned. If you wish to use it also on server, you'd have to store token in the cookies, and then use some kind of helper to retrieve user data asynchronously based on token in the cookie.
@Roseate Spoonbill Bear in mind though, global store will be accessible only on client side if implemented the way I mentioned. If you wish to use it also on server, you'd have to store token in the cookies, and then use some kind of helper to retrieve user data asynchronously based on token in the cookie.
American ShorthairOP
I heard storing token in local storage is not good practise as it is not that secure and can be grabbed easily. And global stores in next.js needs providers/wrappers which turn all child components into a client components
That is another reason I decided to avoid using global stores
And in case of cookie I can not access http only cookie on client components when I need to pass some data to fetch requests
Roseate Spoonbill
You can reuse the cookie if you do fetch with credentials, as long as cors policy of the target allows it. Pretty much any client-side state can be grabbed easily, co if you need that token to be on client, there is no safe storage for it. Least accessible for attacker is in-memory state store (mentioned
HTTP-only cookie is the safest way to go, but only if you don't need to use that token on your JS client code and if you can use client-side
So as much as you might've heard that
zustand
and similar libraries), but even then it's not bulletproof and it creates usability issue, where refreshing page will log you out of the system. HTTP-only cookie is the safest way to go, but only if you don't need to use that token on your JS client code and if you can use client-side
fetch
with credentials: "include"
. So as much as you might've heard that
localStorage
is not safe, both local and session storage are still commonly used for tokens or refresh tokens, as they don't have good alternative.@Roseate Spoonbill You can reuse the cookie if you do fetch with credentials, as long as cors policy of the target allows it. Pretty much any client-side state can be grabbed easily, co if you need that token to be on client, there is no safe storage for it. Least accessible for attacker is in-memory state store (mentioned `zustand` and similar libraries), but even then it's not bulletproof and it creates usability issue, where refreshing page will log you out of the system.
HTTP-only cookie is the safest way to go, but only if you don't need to use that token on your JS client code and if you can use client-side `fetch` with `credentials: "include"`.
So as much as you might've heard that `localStorage` is not safe, both local and session storage are still commonly used for tokens or refresh tokens, as they don't have good alternative.
American ShorthairOP
I am thinking what if I replace bearer token with credentials include and pass cookie to the server instead of using access/refresh token auth logic, But not sure if it is going to be good idea
Roseate Spoonbill
It's going to be inaccessible in JS, so will be safer. Good/Bad idea are relative terms, and only you can know if it will suite your application/cors policies etc.
@Roseate Spoonbill It's going to be inaccessible in JS, so will be safer. Good/Bad idea are relative terms, and only you can know if it will suite your application/cors policies etc.
American ShorthairOP
What if I use server actions for client side data fetching? Server actions can access cookie right? And I take token from the cookie and pass it to the request
But thing is it may act like a proxy, I use external backend
Roseate Spoonbill
yup, server actions can use cookies.
But thing is it may act like a proxy, I use external backend
Yes, this is often how apps work if they need to access external backend and need to protect access to it. Finally, if you want to be super-safe-and-strict you can encrypt that cookie in a way that only your backend can read it and it's specific to user agent.
American ShorthairOP
I encrypt cookies in external backend yeah. But thing is server actions are not always comforatble when you do not have forms but rather api calls on events or button clicks
Roseate Spoonbill
For the best ideas, I think you'd have to ask more targeted question specifying:
- what is your data source
- how users authenticate
- where you fetch data with authentication (client/server)
- what is your data source
- how users authenticate
- where you fetch data with authentication (client/server)
@American Shorthair I encrypt cookies in external backend yeah. But thing is server actions are not always comforatble when you do not have forms but rather api calls on events or button clicks
Roseate Spoonbill
You can do it without forms and simply call the function, I've done this multiple times with different setups. Server action doesn't even need formData as argument. Take a look in the docs to see how they can be used: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#behavior
American ShorthairOP
Alright man thanks a lot
American ShorthairOP
One more question if you do not mind @Roseate Spoonbill, how do you reset the server action state? I return data when user completes the form but then on button click I want to reset the form data which I get from useActionState to initial values
Roseate Spoonbill
There is couple of ways you can deal with it. The simplest would probably be something like this on client side:
But it depends a lot on how your current code works and how you validate the successful submission. You can also return empty values on successful requests and use those as values inside the form.
const formRef = useRef(); // ref to the form element
const [...] = useActionState(async function() {
return someServerAction().then((response) => {
// additionally check if submission was successful
formRef.current.reset();
})
}, someDefaultValues);
But it depends a lot on how your current code works and how you validate the successful submission. You can also return empty values on successful requests and use those as values inside the form.
@Roseate Spoonbill There is couple of ways you can deal with it. The simplest would probably be something like this on client side:
javascript
const formRef = useRef(); // ref to the form element
const [...] = useActionState(async function() {
return someServerAction().then((response) => {
// additionally check if submission was successful
formRef.current.reset();
})
}, someDefaultValues);
But it depends a lot on how your current code works and how you validate the successful submission. You can also return empty values on successful requests and use those as values inside the form.
American ShorthairOP
What I mean is, let's say I get data from form action and it is saved in state like in this case
I want to clear the state from different component on button click how can I do that?
I came up with creating useState and I store returned state from form action into that useState and later when I want to clear state I just set useState
const [state, formAction, pending] = useActionState(
checkPrimoAction,
formState
);
I want to clear the state from different component on button click how can I do that?
I came up with creating useState and I store returned state from form action into that useState and later when I want to clear state I just set useState
const Home = () => {
//* useState
const [formState, setFormState] = useState({
message: "",
status: PRIMO_STATUS.INITIAL,
});
const [state, formAction, pending] = useActionState(
checkPrimoAction,
formState
);
//* useEffect
useEffect(() => {
if (state.status !== PRIMO_STATUS.INITIAL) {
setFormState({
message: state.message,
status: state.status,
});
}
}, [state]);
return (
<main className="max-500:w-full grow m-auto flex items-center justify-center">
{formState.status === PRIMO_STATUS.INITIAL ? (
<CheckPrimo formAction={formAction} pending={pending} />
) : (
<Status type={formState.status} setFormState={setFormState} />
)}
</main>
);
};
export default Home;
Roseate Spoonbill
This is probably one of the ways to do it. Personally I'd try first with
<button type="reset" form={formid}>Reset</button>
and then give both the button and the form the id generated with useId
hook.It's more of a pure html solution, but I do the same with externalized submission buttons. Reset would probably work a well. To that note, that's specifically for form submission/reset. For other cases, sharing state through props, context, or with state library might be the only option