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

feat(utils/useHydrateAtoms) - Optionally rehydrate with force hydrate #1990

Merged

Conversation

SariSabouh
Copy link
Contributor

Related Issues or Discussions

The useHydrateAtom discussion has been live since August of 2021 in #669.

Summary

We found a use case within our project that required a rehydration after a specific event. We had to manually rely on conditional useEffect actions to manually update the store rather than hydrating back from a fully reset SSR page. After review, a forceHydration option meets that requirement and a lot of the cases mentioned within that option discussion.

Check List

  • yarn run prettier for formatting code and docs

@vercel
Copy link

vercel bot commented Jun 14, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
jotai ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 15, 2023 2:42pm

@codesandbox-ci
Copy link

codesandbox-ci bot commented Jun 14, 2023

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit c4c5e4c:

Sandbox Source
React Configuration
React Typescript Configuration
React Browserify Configuration
React Snowpack Configuration
Next.js Configuration
Next.js with custom Babel config Configuration
React with custom Babel config Configuration

Copy link
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your suggestion. Added some comments.

src/react/utils/useHydrateAtoms.ts Outdated Show resolved Hide resolved
src/react/utils/useHydrateAtoms.ts Outdated Show resolved Hide resolved
@SariSabouh SariSabouh requested a review from dai-shi June 15, 2023 13:47
@SariSabouh
Copy link
Contributor Author

Added all requested changes @dai-shi

@dai-shi dai-shi changed the title feat(utils) - Optionally rehydrate with force hydrate feat(utils/useHydrateAtoms) - Optionally rehydrate with force hydrate Jun 15, 2023
@dai-shi
Copy link
Member

dai-shi commented Jun 15, 2023

On second thought, we could do and suggest to use store api for such use cases.

const Component = () => {
  const store = useStore()
  store.set(countAtom, countValue)
  // ...
}

What do you think?

@SariSabouh
Copy link
Contributor Author

On second thought, we could do and suggest to use store api for such use cases.

const Component = () => {
  const store = useStore()
  store.set(countAtom, countValue)
  // ...
}

What do you think?

Basically disregard the forceHydrate and suggest using the store API? If yes, that could technically fully replace the useHydrateAtom though and may cause many re-renders in some cases, especially if its an object compare.

@dai-shi
Copy link
Member

dai-shi commented Jun 16, 2023

Basically disregard force hydrate, but store api wouldn't fully replace useHydreteAtoms which ensures only setting values if unset.

may cause many re-renders in some cases

I'm not sure if I get those cases.

@SariSabouh
Copy link
Contributor Author

Basically disregard force hydrate, but store api wouldn't fully replace useHydreteAtoms which ensures only setting values if unset.

may cause many re-renders in some cases

I'm not sure if I get those cases.

I can try to re-produce it again but I remember going for the forceHydrate because useStore was was causing a re-render. Is there a reason you're opposed to going with some sort of re-hydrate method? I was thinking we can integrate atomsWithReset somehow instead.

@dai-shi
Copy link
Member

dai-shi commented Jun 16, 2023

The reason to be against dangerouslyForceHydrate (the name is actually good to warn the usage) is that it bypasses the hydration check, and the result is just equal to store.set(...). It means we provide two ways for the same capability which we carefully avoid in Jotai API design.

@SariSabouh
Copy link
Contributor Author

SariSabouh commented Jun 16, 2023

So you are suggesting that we do something like this:

export const Component = ({
  componentPreferences,
}: ComponentPageProps) => {
  useHydrateAtoms([[componentPreferencesAtom, componentPreferences]])
  const store = useStore()
  componentPreferencesValue.reset &&
    store.set(componentPreferencesAtom, componentPreferences)

Which this code removes the need for useHydrateAtoms because .reset could be controlled that its reset only at the need for a re-hydrate

@dai-shi
Copy link
Member

dai-shi commented Jun 16, 2023

I'm not sure if I understand the purpose of "reset", but my suggestion was if you use store.set() you don't use useHydrateAtoms.

Ah, I see you want to force hydrate if reset is true, and otherwise, only hydrate initially.
Hm, that use case supports this PR's change.
My suggestion to go against this PR would require store.has() method which doesn't exist.

@SariSabouh
Copy link
Contributor Author

I'm open to ideas. The issue is that we hydrate a page with data that is needed until a certain action completion. Once this action is completed, we want to fully reset that atom and get new data from SSR on next time this page loads.

@dai-shi
Copy link
Member

dai-shi commented Jun 16, 2023

I feel like merging this PR as the code is clean and it has dangerously prefix to warn the usage.
After all, it is in jotai/utils which is a little less strict about API design.

we want to fully reset that atom and get new data from SSR on next time this page loads.

I'm curious about your case. When you get new data from SSR on next time, how would the store be kept in memory (assumed so because you want to fully reset) ?

@SariSabouh
Copy link
Contributor Author

We have a store instance that stays maintained, and the only thing we fully reset is that specific atom.

So basically here is the flow:

  1. Store is create at app init
  2. User reaches an SSR page called X
  3. SSR pages gets hydrated using useHydrateAtoms based on data retrieved from APIs
  4. If the user navigates between pages back and forth, we do not want to re-hydrate, as designed by Jotai
  5. If the user goes back to page X, and completes a certain action
  6. The hydrated atom on page X should be fully reset and re-hydratable.

That is how we got to where we are. I was hoping atomWithReset will take care of that, but based on Jotai code it does not as nothing now can re-hydrate. Hence this PR.

@dai-shi
Copy link
Member

dai-shi commented Jun 16, 2023

and completes a certain action

Hmm? If this is a user action, we should update atoms at the same time. Updating atoms in render is something we should really avoid. It's not just bad practice, but theoretically delayed.

@SariSabouh
Copy link
Contributor Author

So once the user completes the action it takes him out of the page. And they won't get the newest set of data until they reach that page.

Like once you complete checkout, you are taken out of checkout and your checkout data is reset. However, once you come back from checkout, you need the newest set of data from the server to be hydrated again. Client side won't know the data until they reach checkout, which is SSR.

@dai-shi
Copy link
Member

dai-shi commented Jun 16, 2023

I'm not exactly sure how the second SSR&hydration work, but it sounds like you can only get the new data from props. In that case, the render time updating wouldn't be avoidable. Can't be helped because you don't have control, right? Sounds fair enough, because we usually suggest useEffect for sync with props, but that's far from ideal.

@SariSabouh
Copy link
Contributor Author

Exactly. We want to avoid the useEffect as it was adding unnecessary complexity and conditions that are not needed. I agree with the hydrate once design, but a reset after checkout seems common enough.

@dai-shi
Copy link
Member

dai-shi commented Jun 16, 2023

I think there should be a better way, probably atoms-in-atom or jotai-molecules, and we want to educate such ones for common use cases.

This PR change helps as an escape hatch for edge cases, so I want to merge, but we shouldn't teach people to use it for common cases.

Copy link
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the discussion. It was convincing. I will merge it later.

@SariSabouh
Copy link
Contributor Author

I think there should be a better way, probably atoms-in-atom or jotai-molecules, and we want to educate such ones for common use cases.

This PR change helps as an escape hatch for edge cases, so I want to merge, but we shouldn't teach people to use it for common cases.

Haven't messed with jotai-molecules yet, but definitely will give them a shot!

@dai-shi dai-shi added this to the v2.2.1 milestone Jun 18, 2023
@dai-shi dai-shi merged commit 358b873 into pmndrs:main Jun 18, 2023
kodiakhq bot referenced this pull request in sullivanpj/open-system Jun 26, 2023
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [jotai](https://togithub.com/pmndrs/jotai) | [`2.1.0` -> `2.2.1`](https://renovatebot.com/diffs/npm/jotai/2.1.0/2.2.1) | [![age](https://badges.renovateapi.com/packages/npm/jotai/2.2.1/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/jotai/2.2.1/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/jotai/2.2.1/compatibility-slim/2.1.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/jotai/2.2.1/confidence-slim/2.1.0)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>pmndrs/jotai (jotai)</summary>

### [`v2.2.1`](https://togithub.com/pmndrs/jotai/releases/tag/v2.2.1)

[Compare Source](https://togithub.com/pmndrs/jotai/compare/v2.2.0...v2.2.1)

This includes some improvements in `jotai/utils`. Especially, `unstable_unwrap` is getting to be stable.

##### What's Changed

-   feat(utils/useHydrateAtoms) - Optionally rehydrate with force hydrate by [@&#8203;SariSabouh](https://togithub.com/SariSabouh) in [https://github.com/pmndrs/jotai/pull/1990](https://togithub.com/pmndrs/jotai/pull/1990)
-   fix(utils): revert atomWithStorage typing by [@&#8203;dai-shi](https://togithub.com/dai-shi) in [https://github.com/pmndrs/jotai/pull/1994](https://togithub.com/pmndrs/jotai/pull/1994)
-   fix(utils): improve selectAtom typing by [@&#8203;dai-shi](https://togithub.com/dai-shi) in [https://github.com/pmndrs/jotai/pull/1995](https://togithub.com/pmndrs/jotai/pull/1995)
-   fix(utils): improve unstable_unwrap for sometimes async atom by [@&#8203;dai-shi](https://togithub.com/dai-shi) in [https://github.com/pmndrs/jotai/pull/1996](https://togithub.com/pmndrs/jotai/pull/1996)
-   fix(utils): unstable_unwrap to support writable atom by [@&#8203;dai-shi](https://togithub.com/dai-shi) in [https://github.com/pmndrs/jotai/pull/1997](https://togithub.com/pmndrs/jotai/pull/1997)

##### New Contributors

-   [@&#8203;SariSabouh](https://togithub.com/SariSabouh) made their first contribution in [https://github.com/pmndrs/jotai/pull/1990](https://togithub.com/pmndrs/jotai/pull/1990)

**Full Changelog**: pmndrs/jotai@v2.2.0...v2.2.1

### [`v2.2.0`](https://togithub.com/pmndrs/jotai/releases/tag/v2.2.0)

[Compare Source](https://togithub.com/pmndrs/jotai/compare/v2.1.1...v2.2.0)

It includes a few improvements. Some utils are rewritten as there was a misconception when migrating from v1. ESM builds are optimized for Vite users.

#### Migration Guide for `jotai/utils`

##### `atomWithDefault`

```js
// suppose we have this
const asyncAtom = atom(() => Promise.resolve(1))
const countAtom = atomWithDefault((get) => get(asyncAtom))
// and in component
const setCount = useSetAtom(countAtom)

// previously,
setCount((c) => c + 1) // it worked, but it will no longer work

// instead, you need to do this
setCount((countPromise) => countPromise.then((c) => c + 1))
```

##### `atomWithStorage`

```js
// suppose we have async storage
const storage = createJSONStorage(() => AsyncStorage)
const countAtom = atomWithStorage('count-key', 0, storage)
  // in component
  const [count, setCount] = useAtom(countAom)

  // previously, countAtom is a sync atom, so you could update like this:
  setCount((c) => c + 1)

  // with the new version, it becomes async occasionally, so you need to resolve it:
  setCount(async (c) => (await c) + 1)
```

#### What's Changed

-   breaking(utils): predictable atomWithDefault by [@&#8203;dai-shi](https://togithub.com/dai-shi) in [https://github.com/pmndrs/jotai/pull/1939](https://togithub.com/pmndrs/jotai/pull/1939)
-   breaking(utils): improve atomWithStorage by [@&#8203;dai-shi](https://togithub.com/dai-shi) in [https://github.com/pmndrs/jotai/pull/1958](https://togithub.com/pmndrs/jotai/pull/1958)
-   feat(vanilla): new store listeners for devtools by [@&#8203;dai-shi](https://togithub.com/dai-shi) in [https://github.com/pmndrs/jotai/pull/1966](https://togithub.com/pmndrs/jotai/pull/1966)
-   fix(build): mode env for "import" condition" by [@&#8203;dai-shi](https://togithub.com/dai-shi) in [https://github.com/pmndrs/jotai/pull/1978](https://togithub.com/pmndrs/jotai/pull/1978)

#### New Contributors

-   [@&#8203;reinierkaper-carewell](https://togithub.com/reinierkaper-carewell) made their first contribution in [https://github.com/pmndrs/jotai/pull/1980](https://togithub.com/pmndrs/jotai/pull/1980)

**Full Changelog**: pmndrs/jotai@v2.1.1...v2.2.0

### [`v2.1.1`](https://togithub.com/pmndrs/jotai/releases/tag/v2.1.1)

[Compare Source](https://togithub.com/pmndrs/jotai/compare/v2.1.0...v2.1.1)

This version fixes some issues in edge cases.

#### What's Changed

-   fix(vanilla): Stable promise by [@&#8203;backbone87](https://togithub.com/backbone87) in [https://github.com/pmndrs/jotai/pull/1933](https://togithub.com/pmndrs/jotai/pull/1933)
-   fix(vanilla): update atoms with tree structure dependencies (regression from v1) by [@&#8203;dai-shi](https://togithub.com/dai-shi) in [https://github.com/pmndrs/jotai/pull/1959](https://togithub.com/pmndrs/jotai/pull/1959)
-   fix: prefer PromiseLike where appropriate by [@&#8203;dai-shi](https://togithub.com/dai-shi) in [https://github.com/pmndrs/jotai/pull/1967](https://togithub.com/pmndrs/jotai/pull/1967)

#### New Contributors

-   [@&#8203;blissdev](https://togithub.com/blissdev) made their first contribution in [https://github.com/pmndrs/jotai/pull/1945](https://togithub.com/pmndrs/jotai/pull/1945)
-   [@&#8203;hwanyoungChoi](https://togithub.com/hwanyoungChoi) made their first contribution in [https://github.com/pmndrs/jotai/pull/1957](https://togithub.com/pmndrs/jotai/pull/1957)
-   [@&#8203;alexhad6](https://togithub.com/alexhad6) made their first contribution in [https://github.com/pmndrs/jotai/pull/1971](https://togithub.com/pmndrs/jotai/pull/1971)
-   [@&#8203;backbone87](https://togithub.com/backbone87) made their first contribution in [https://github.com/pmndrs/jotai/pull/1933](https://togithub.com/pmndrs/jotai/pull/1933)

**Full Changelog**: pmndrs/jotai@v2.1.0...v2.1.1

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/sullivanpj/open-system).
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