Skip to content

feat!: implement injectHydration; remove manualHydration #195

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

Merged
merged 1 commit into from
Feb 28, 2025

Conversation

bowheart
Copy link
Collaborator

@affects atoms, stores

Description

A missing piece of signals is manual hydration. injectSignal doesn't have any sort of hydrate config option currently, even though I mentioned it in the v2 spec. I hadn't decided whether we needed to tackle this before v2 or if Zedux's automatic hydration was good enough that we could add it in a minor version later.

Several recent requests for better SSR support and documentation made me revisit this now.

After long debate around how hydrating signals will work, considering current pain points with hydration and new challenges signals themselves introduce, I've decided we finally need a full, dedicated hydration injector.

Implement injectHydration. This offers full flexibility over when/how you use the hydration. It can be used in combination with third-party hydrators to easily determine priority, especially in local-first setups where url state, localStorage, and similar client storage mechanisms are involved.

Example:

const multiSourcedAtom = atom('multiSourced', () => {
  const fromServer = injectHydration<string>()

  // two example third-party injectors:
  const fromLocalStorage = injectLocalStorageHydration<string>()
  const fromUrl = injectUrlHydration<string>()

  const signal = injectSignal(
    fromUrl ?? fromLocalStorage ?? fromServer ?? 'the default value'
  )

  return signal
})

As always, these injectors can be consolidated into a single custom injector:

const signal = injectLocalFirstSignal('the default value')

injectHydration takes an optional config object with two options:

  • intercept - the first time injectHydration is called in an atom, it disables automatic hydration. Zedux assumes you're using the injected hydration to hydrate manually. This should pretty much always be what you want, but if not, you can prevent this behavior with intercept: false.
  • transform - if the atom defines a hydrate config function, it's called to transform the hydration by default. Pass transform: false to prevent this.

Breaking Change

Remove the manualHydration atom config option, as per the v2 spec. Automatic hydration is now disabled if injectHydration is called in the atom state factory without { intercept: false }.

Also kill the concept of "consuming hydrations" (deleting them after first use). Hydrations will now stay on ecosystem.hydration by default, allowing them to be reused if the atom is destroyed and recreated. This can be reimplemented manually with an ecosystem event listener on the cycle event:

const cleanup = ecosystem.on('cycle', event => {
  // you can check specific ids or template keys (`event.source.template.key`):
  if (event.source?.id && ecosystem.hydration[event.source.id]) {
    // you are free to mutate this object:
    delete ecosystem.hydration[event.source.id]
    // you can call `cleanup` after all expected nodes have been hydrated
  }
})

@bowheart bowheart merged commit 64c08a8 into master Feb 28, 2025
2 checks passed
@bowheart bowheart deleted the josh/injectHydration branch February 28, 2025 18:44
@bowheart bowheart added this to the Zedux v2 milestone Mar 4, 2025
@bowheart bowheart mentioned this pull request Mar 13, 2025
53 tasks
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.

1 participant