Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add defaultServerValue property #74

Merged
merged 1 commit into from
Nov 13, 2024
Merged

Conversation

BatuhanTopcu
Copy link

Reason

we have a modal to defaults to open if local storage value does not exists. If value for opened before is undefined we want to open modal. using defaultValue to set key to false causes modal to flashes on top that. we tried setting different values for client and server in defaultValue using it as function.

const [state, setState] = useLocalStorageState(
    'key',
    {
      defaultValue() {
        if (isServer) {
          return "server default value";
        }
        return "client default value";
      },
    },
  );

code above prevents modal first opening then closing as flash but now causes hydration errors.
fix is using getServerSnapshot to differ client and server defaults. getServerSnapshot function runs during SSR and first hydration so it prevents that errors. See docs

What is done

  • Added field defaultServerValue
  • Make sure that it not brokes current versions
  • Added tests for client and server
  • Added readme

@astoilkov
Copy link
Owner

astoilkov commented Nov 11, 2024

Hey, thanks for your contribution!

Is it ok for me to ask some questions? I'm not sure I understand. Can you give me a code snippet example that doesn't work with the current API that will work if there is a getServerSnapshot option?

I'm just trying to imagine your codebase and I can't. For example, I guess I will write the following code if I will need to do what I understand you are describing:

const [showModal, setShowModal] = useLocalStorageState('show-modal', {
  defaultValue: false
})

if (!showModal) {
  return null
}

return <div>modal</div>

How should I change the example below so it now needs a new option in order to work property (or you can just show a different example, if that's easier)?

Thanks in advance!

@astoilkov
Copy link
Owner

astoilkov commented Nov 11, 2024

Also, I see two people liked the PR — @kadirgombel and @damla.

Do you have the same use case as well? Can you explain your need for such a property?

Thanks!

@BatuhanTopcu
Copy link
Author

BatuhanTopcu commented Nov 11, 2024

Hello, they are my coleagues.

Example:

const showWelcomeModal, setShowWelcomeModal] = useLocalStorageState('welcome-modal', {
  defaultValue: true // we want to show welcome if value does not exist in local storage
})

if (!showModal) {
  return null
}

return <div>welcome</div>

code below has an issue server always shows modal with SSR response but if welcome-modal exists in local storage when client takes over it hides modal. so when I tried to change default value on defaultValue based on environment it fixes that but then throws hydration error.

Thank you!

@astoilkov
Copy link
Owner

Sorry, I still don't understand. I will try to break this down and give you a CodeSandbox that you can edit so we are on the same page.

Can you edit this CodeSandbox so you make the modal div flicker?

Also, do you know that you can conditionally give a different defaultValue as you tried but using useSyncExternalStore() hook?:

const isServerOrHydrating = useSyncExternalStore(
  () => {
    return () => {};
  },
  () => false,
  () => true
);
const [showWelcomeModal, setShowWelcomeModal] = useLocalStorageState('welcome-modal', {
      defaultValue() {
        if (isServerOrHydrating) {
          return "server default value";
        }
        return "client default value";
      },
})

I hope you understand me. I want to understand it before merging it.

@BatuhanTopcu
Copy link
Author

Hello, yep there is no problem. https://codesandbox.io/p/devbox/suspicious-meninsky-forked-qf2pgw
I added two cases:

  1. User saw onboarding modal before, so shouldn't be saw it again
    If I set defaultValue: false and user's first enter, its written to localStorage in getSnapshot side of this lib so user can't see modal at all.
    If I set defaultValue: true and if user entered before, modal is rendered on server then removed from client (Flicker issue I meant)

  2. I did not know isServerOrHydrating hack you said It looked promised but has the same issue

const [showWelcomeModal] = useLocalStorageState("case-2", {
    defaultValue: () => {
      if (isServerOrHydrating) return false;
      return true;
    },
 });

this will work in users recurrent enters to page without flicker so it is fixing that issue.
but in the first entrance defaultValue: false written to localStorage while hydration so user not see modal at all.

I tried to simulate these states with useLayoutEffect but not sure that works, you can remove them and set local storage by yourself

I hope it was understandable; thanks for your time.

@astoilkov
Copy link
Owner

Nice! I think I got it.

So what you are trying to accomplish is this — https://codesandbox.io/p/devbox/suspicious-meninsky-forked-yz8zfr?workspaceId=d4f0087a-bf2e-4c09-a4d6-c87fd0d77ed8, right?

The example shows how to show the modal but not render it on server or while hydrating to avoid flicker and hydration errors. If you manually call localStorage.setItem('show-welcome-modal, 'false')`, this will still not render the modal on the server or while hydrating and won't show it on the client as well.

That's it right?

@BatuhanTopcu
Copy link
Author

Hello again,

I can't access the codesandbox but you are right, If I use

const [showWelcomeModal] = useLocalStorageState("case-2", {
    defaultValue: () => {
      if (isServerOrHydrating) return false;
      return true;
    },
 });

there will be no flicker and hydration error but issue with this is defaultValue sets given value to localStorage so user can't see the modal at all

@astoilkov
Copy link
Owner

@BatuhanTopcu
Copy link
Author

yep it's what I accomplish thank you!
so should I use this hackish solution or can we merge this PR?
thanks a lot!

@astoilkov
Copy link
Owner

I'll merge the PR. After I took the time to understand it, it seems like a good idea.

Thanks for your contribution!

I will try to make a release soon.

@astoilkov astoilkov merged commit 85c42fc into astoilkov:main Nov 13, 2024
2 checks passed
@BatuhanTopcu
Copy link
Author

Thanks a lot for spending your time,
This is my first contribution to open-source, and that felt awesome!

@astoilkov
Copy link
Owner

The release is ready.

It was fun for me as well! Your first contribution is really well done, keep the good work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants