Confused about Next.js implementations #168
-
Hey, can someone show me how can I define a global observable the Legend way in Next.js (I'm using a client component), which I can update onClick. AND that's for me the part that I think I especially don't understand: How the hell I can show an image based on this observable? Thank you for your answers, because I'm really frustrated now and really don't want to go back to Zustand. I always got hydration errors next.js crashed or it simply didn't work. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 9 replies
-
Could you share some code, would be easier to review that way. |
Beta Was this translation helpful? Give feedback.
-
@jmeistrich |
Beta Was this translation helpful? Give feedback.
-
I've currently been using the following, in order to avoid hydration errors for Next.js (I am building for static export though, so no idea how it works with SSR): import { Selector } from '@legendapp/state';
// eslint-disable-next-line no-restricted-imports
import { useObservable, useSelector as useSelectorInternal, UseSelectorOptions } from '@legendapp/state/react';
import { useMount } from '@legendapp/state/react';
/**
* Hook to track if the component can render.
*
* Returns true if the component can render, false otherwise.
*
* https://github.com/LegendApp/legend-state/discussions/168#discussioncomment-6616457
*/
function useCanRender(): boolean {
const canRenderState = useObservable(false);
const canRender = useSelectorInternal(canRenderState);
useMount(() => {
canRenderState.set(true);
});
return canRender;
}
/**
* Workaround to avoid hydration errors when using the useSelector hook.
*
* https://github.com/LegendApp/legend-state/discussions/168#discussioncomment-6616457
*/
export function useSelector<T>(selector: Selector<T>, options?: UseSelectorOptions): T | null {
const canRender = useCanRender();
const value = useSelectorInternal(selector, options);
return canRender ? value : null;
} This uses the trick from #168 (reply in thread) where we first check if the component has been mounted (only happens on Client-side) and only then returns the value from the observable. An unfortunate side-effect is that all selectors can now be The I've then disabled using {
// ...
rules: [
{
name: '@legendapp/state/react',
importNames: ['useSelector'],
message: 'Please use useSelector from @/hooks/use-selector instead which avoids hydration errors.',
},
],
// ...
} It definitely doesn't feel ideal, but the hydration issues were causing bugs so it'll have to do until there's a better approach :/ |
Beta Was this translation helpful? Give feedback.
Oh, there can sometimes be a hydration error when using
persistObservable
because that is local-only state. It should run ok but just display a warning in development mode. To fix that you could remove the persistence, or make sure it's running client-only.What we do is make sure our components that use persisted state run client-side only. See:
https://nextjs.org/docs/messages/react-hydration-error#solution-1-using-useeffect-to-run-on-the-client-only
We have this
useCanRender
hook for this purpose: