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

[SSR/Astro] DOM Not Updated from State by Client Updates onMount #2154

Closed
NexRX opened this issue May 5, 2024 · 5 comments
Closed

[SSR/Astro] DOM Not Updated from State by Client Updates onMount #2154

NexRX opened this issue May 5, 2024 · 5 comments

Comments

@NexRX
Copy link

NexRX commented May 5, 2024

Describe the bug

When SSRing with state and that state is updated on the client immediately/onMount, the new value is not reflected in the DOM.

Your Example Website or App

https://stackblitz.com/~/github.com/NexRX/astro-solid-nanostore

Steps to Reproduce the Bug or Issue

Using my example which demos a simple theme toggle defaulting to 'dark' mode...

  1. Click the toggle to change the state to 'light' mode.
  2. Reload the page
  3. Observe page reflects 'light' mode (because store subscriber is changing this) but controls show the 'dark' mode.
    a. Press the button to console log state which shows store actually holds 'light'

Expected behavior

When the client takes over after SSR the DOM reflect the updated state from onMount

Screenshots or Videos

Issue Demo Gif

Platform

  • OS: Windows 11
  • Browser: Chrome
  • Version: 124

Additional context

  • You can afterward toggle it a couple times which fixes the issues only after.
  • When rendering with astro using client:only the problem goes away (but not ideal)
  • The nanostore-persistant uses nanostore's version of onMount but I did fork and try using solid-js's onMount to the same effect
@NexRX
Copy link
Author

NexRX commented May 16, 2024

Hi all, this has been sitting here for a bit now. Anything I can do to contribute to fixing this? :)

@ryansolid
Copy link
Member

ryansolid commented May 17, 2024

Well trying to decide what to do here. Solid is acting exactly how I'd expect it to. But I can see why the situation is awkward. Server renders dark, it hydrates thinking light, it assumes it matches which it doesn't and we don't do corrections. Then when you toggle it to light.. the client already thinks it is light so it doesn't propagate any change.. You have to change it to dark to change it to light again.

Your best bet is to have the server and client always match on initial render and then have the localStorage kick in. Which means it needs to hydrate dark. Which means nano-stores persistent atom isn't helping you here.

Changing your component to this works:

const nanoTheme = useStore($theme);
const [theme, setTheme] = createSignal("dark")
createEffect(() => {
   setTheme(nanoTheme())
})

return (<li class="link-card">
        <h2>
            Active Theme
        </h2>
        <p>
            {theme()} <br />
            Dark?: 
            <input type="checkbox" checked={theme() === "dark"} onChange={(e) => {
                $theme.set(e.target.checked ? "dark" : "light")
            }} />
            <br />
            <button onClick={() => console.log(`Atom value = '${$theme.get()}' | Store value = '${theme()}'`)}>Log Theme State</button>
        </p>
    </li>

But it does feel a bit like it isn't leveraging the the niceness of what nanostores is trying to offer. That being said persistent store as a source of truth with hydration seems like it isn't a good pattern in general.

@ryansolid
Copy link
Member

Yeah the more I look at this the more I can't say this is a bug. Persistent storage is always going to cause rendering to not match unless you apply it after mount/hydration.

@NexRX
Copy link
Author

NexRX commented May 20, 2024

Ah okay, thanks for the insight at least. What would be your goto for applying after mount? I tried using createEffect/onMount but had no luck. I assume you mean post the point of a onMount triggering and running?

@ryansolid
Copy link
Member

No I meant onMount or createEffect. I edited the example and it seemed to switch fine. The problem is that the persistent nanostores start in the "correct" state which is wrong for hydration. So the server rendering and the client have to start at the fixed value and then apply the localStorage after. Typically in Solid I'd just be doing this by directly grabbing from localStorage in a onMount but given that the desire in your example was for nanostores to stay in sync I did that sort of createEffect syncing thing. Since I can't imagine this toggle is a hyper performance area the fact it writes in an effect should be fine.

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

No branches or pull requests

2 participants