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: add useShallow #2090

Merged
merged 13 commits into from
Oct 2, 2023
Merged

feat: add useShallow #2090

merged 13 commits into from
Oct 2, 2023

Conversation

FaberVitale
Copy link
Contributor

@FaberVitale FaberVitale commented Sep 30, 2023

Summary

Adds a useShallow hook to prevent unnecessary re-renders when a selector outputs the same value according to shallow equal.

Basic usage

import { create } from 'zustand'
import { useShallow } from 'zustand/shallow'

const useMyStore = create(() => ({ a: 1, b: 2, c: 3 }))

export const TestShallow = () => {
  const output = useMyStore(useShallow(Object.keys))

  return <div>{output.join(',')}</div>
}

See tests in shallow.test.tsx for more details.


Related Issues or Discussions

Check List

  • yarn run prettier for formatting code and docs

@vercel
Copy link

vercel bot commented Sep 30, 2023

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

Name Status Preview Comments Updated (UTC)
zustand-demo ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 2, 2023 1:09pm

@codesandbox-ci
Copy link

codesandbox-ci bot commented Sep 30, 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 886bf51:

Sandbox Source
React Configuration
React TypeScript Configuration
React Browserify Configuration
React Snowpack Configuration
React Parcel Configuration
Next.js Configuration
@pavlobu/zustand demo Configuration

src/shallow.ts Outdated
? (prev.current as U)
: (prev.current = next)
},
[selector]
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need useMemo nor useCallback for this, because Zustand doesn't require stable selector.

Do you prefer useRef? I think we should use useMemo like #1937 (reply in thread) , because React may prohibit updating ref during render in the future (L73 is usually not allowed).

Copy link
Contributor Author

@FaberVitale FaberVitale Sep 30, 2023

Choose a reason for hiding this comment

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

Version 1 of #1937 (reply in thread) requires useCallback to use inline selectors.

Version 2 of #1937 (reply in thread) works but we open the door to potential stale closure issues.

If there are no better alternatives version 2 is better.

Are there less cumbersome alternatives?


I wish we could use useEvent or something similar during the render phase :/

Copy link
Member

Choose a reason for hiding this comment

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

Oops, that's my bad. I forgot about it. Let's use useRef then.

I think you can remove useMemo from your implementation.

Copy link
Member

Choose a reason for hiding this comment

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

It's not necessary because the caller of useShallow can always wrap the selector with useCallabck.

We never need useCallback, it doesn't improve anything.

@dai-shi
Copy link
Member

dai-shi commented Sep 30, 2023

Basic usage

create returns a hook. So, let's do this.

import { create } from 'zustand'
import { useShallow } from 'zustand/shallow'

const useMyStore = create(() => ({ a: 1, b: 2, c: 3 }))

export const TestShallow = () => {
  const output = useMyStore(useShallow(Object.keys))

  return <div>{output.join(',')}</div>
}

Maybe, useShallow((state) => Ojbect.keys(state)) might be easier to read even though it can be less efficient.

Comment on lines +65 to +74
export function useShallow<S, U>(selector: (state: S) => U): (state: S) => U {
const prev = useRef<U>()

return (state) => {
const next = selector(state)
return shallow(prev.current, next)
? (prev.current as U)
: (prev.current = next)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

The code looks good to me.

@FaberVitale
Copy link
Contributor Author

@dai-shi do you know why renderHook is not defined here?

It's rather odd.

I can implement those tests without renderHook if needed.

@dai-shi
Copy link
Member

dai-shi commented Sep 30, 2023

Why useShallow does not have a dependency array?

I don't think we need that section.

We need to update docs too. Would you like to work on this PR, or should it be a separate PR?

@dai-shi
Copy link
Member

dai-shi commented Sep 30, 2023

do you know why renderHook is not defined

I think it's because in the production mode.

I can implement those tests without renderHook if needed.

Yeah, that should be the way to go.

@FaberVitale
Copy link
Contributor Author

FaberVitale commented Sep 30, 2023

Why useShallow does not have a dependency array?

I don't think we need that section.

We need to update docs too. Would you like to work on this PR, or should it be a separate PR?

I'll open another PR tomorrow.

Thanks

EDIT:

see #2090 (comment)

@FaberVitale
Copy link
Contributor Author

Added guide in 9526d87.

Let me know if it's ok.

Thanks

@dai-shi
Copy link
Member

dai-shi commented Oct 1, 2023

Added guide in 9526d87.

Let me know if it's ok.

Thanks

The content looks good. But, what's required in #1937 discussion, I think, is that
we should show the use of useShallow everywhere in the docs instead of using createWithEqualityFn and shallow. What do you think?

@FaberVitale
Copy link
Contributor Author

It's only mentioned in the readme (## Selecting multiple state slices), it seems:

I'll update it

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 working on docs too!
Below are some comments.

readme.md Outdated
@@ -86,36 +86,31 @@ const honey = useBearStore((state) => state.honey)

If you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can tell zustand that you want the object to be diffed shallowly by passing the `shallow` equality function.
Copy link
Member

Choose a reason for hiding this comment

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

Needs to revise this line too?

readme.md Outdated Show resolved Hide resolved
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 so much for your contribution!

@dai-shi dai-shi added this to the v4.4.2 milestone Oct 2, 2023
@dai-shi dai-shi merged commit 3cbd468 into pmndrs:main Oct 2, 2023
33 checks passed
bodinsamuel referenced this pull request in specfy/specfy Nov 8, 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 |
|---|---|---|---|---|---|
| [zustand](https://togithub.com/pmndrs/zustand) | [`4.4.1` ->
`4.4.6`](https://renovatebot.com/diffs/npm/zustand/4.4.1/4.4.6) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/zustand/4.4.6?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/zustand/4.4.6?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/zustand/4.4.1/4.4.6?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/zustand/4.4.1/4.4.6?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

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

### [`v4.4.6`](https://togithub.com/pmndrs/zustand/releases/tag/v4.4.6)

[Compare
Source](https://togithub.com/pmndrs/zustand/compare/v4.4.5...v4.4.6)

v4.4.5 has an issue with some TypeScript configs about module
resolution. It should be fixed now. Thanks for the patience.

#### What's Changed

- Update export types by
[@&#8203;dbritto-dev](https://togithub.com/dbritto-dev) in
[https://github.com/pmndrs/zustand/pull/2170](https://togithub.com/pmndrs/zustand/pull/2170)

#### New Contributors

- [@&#8203;cheatkey](https://togithub.com/cheatkey) made their first
contribution in
[https://github.com/pmndrs/zustand/pull/2147](https://togithub.com/pmndrs/zustand/pull/2147)
- [@&#8203;frixaco](https://togithub.com/frixaco) made their first
contribution in
[https://github.com/pmndrs/zustand/pull/2166](https://togithub.com/pmndrs/zustand/pull/2166)

**Full Changelog**:
pmndrs/zustand@v4.4.5...v4.4.6

### [`v4.4.5`](https://togithub.com/pmndrs/zustand/releases/tag/v4.4.5)

[Compare
Source](https://togithub.com/pmndrs/zustand/compare/v4.4.4...v4.4.5)

Hopefully, it should fix some issues with Node.js environment including
Next.js.

#### What's Changed

- fix: importing CJS React in ESM by
[@&#8203;dai-shi](https://togithub.com/dai-shi) in
[https://github.com/pmndrs/zustand/pull/2154](https://togithub.com/pmndrs/zustand/pull/2154)
- Apply publint recommendations by
[@&#8203;dbritto-dev](https://togithub.com/dbritto-dev) in
[https://github.com/pmndrs/zustand/pull/2157](https://togithub.com/pmndrs/zustand/pull/2157)

#### New Contributors

- [@&#8203;plrs9816](https://togithub.com/plrs9816) made their first
contribution in
[https://github.com/pmndrs/zustand/pull/2137](https://togithub.com/pmndrs/zustand/pull/2137)
- [@&#8203;Brammm](https://togithub.com/Brammm) made their first
contribution in
[https://github.com/pmndrs/zustand/pull/2139](https://togithub.com/pmndrs/zustand/pull/2139)
- [@&#8203;sobies93](https://togithub.com/sobies93) made their first
contribution in
[https://github.com/pmndrs/zustand/pull/2142](https://togithub.com/pmndrs/zustand/pull/2142)

**Full Changelog**:
pmndrs/zustand@v4.4.4...v4.4.5

### [`v4.4.4`](https://togithub.com/pmndrs/zustand/releases/tag/v4.4.4)

[Compare
Source](https://togithub.com/pmndrs/zustand/compare/v4.4.3...v4.4.4)

There was a tiny issue in v4.4.3, which broke with some bundlers, which
this version fixes.

#### What's Changed

- fix(build): patch entry points zustand/shallow for CJS by
[@&#8203;dai-shi](https://togithub.com/dai-shi) in
[https://github.com/pmndrs/zustand/pull/2128](https://togithub.com/pmndrs/zustand/pull/2128)

**Full Changelog**:
pmndrs/zustand@v4.4.3...v4.4.4

### [`v4.4.3`](https://togithub.com/pmndrs/zustand/releases/tag/v4.4.3)

[Compare
Source](https://togithub.com/pmndrs/zustand/compare/v4.4.2...v4.4.3)

The changes in v4.4.2 were troublesome for some users. This version
should fix/mitigate such cases.

#### What's Changed

- fix(shallow): Extract shallow vanilla and react by
[@&#8203;dbritto-dev](https://togithub.com/dbritto-dev) in
[https://github.com/pmndrs/zustand/pull/2097](https://togithub.com/pmndrs/zustand/pull/2097)
- fix(types): mitigate devtools typing by
[@&#8203;dai-shi](https://togithub.com/dai-shi) in
[https://github.com/pmndrs/zustand/pull/2099](https://togithub.com/pmndrs/zustand/pull/2099)

**Full Changelog**:
pmndrs/zustand@v4.4.2...v4.4.3

### [`v4.4.2`](https://togithub.com/pmndrs/zustand/releases/tag/v4.4.2)

[Compare
Source](https://togithub.com/pmndrs/zustand/compare/v4.4.1...v4.4.2)

This adds `useShallow` hook to cover some use cases that are deprecated
with v4.4.0 change. Check out [the
guide](https://togithub.com/pmndrs/zustand/blob/ec538e9d4c0b9b5759e6dfd0fd3c9a21f8236949/docs/guides/prevent-rerenders-with-use-shallow.md).

##### Migration Guide

[#&#8203;1991](https://togithub.com/pmndrs/zustand/issues/1991) requires
something like below if you are using the `devtools` middleware *and*
TypeScript.

```diff
  import { devtools } from 'zustand/middleware'
+ import type {} from '@&#8203;redux-devtools/extension'
```

##### What's Changed

- fix(types)(middleware/devtools): avoid copying types by
[@&#8203;dai-shi](https://togithub.com/dai-shi) in
[https://github.com/pmndrs/zustand/pull/1991](https://togithub.com/pmndrs/zustand/pull/1991)
- fix(traditional): make defaultEqualityFn optional in TS Types by
[@&#8203;charkour](https://togithub.com/charkour) in
[https://github.com/pmndrs/zustand/pull/2060](https://togithub.com/pmndrs/zustand/pull/2060)
- feat: add useShallow by
[@&#8203;FaberVitale](https://togithub.com/FaberVitale) in
[https://github.com/pmndrs/zustand/pull/2090](https://togithub.com/pmndrs/zustand/pull/2090)

##### New Contributors

- [@&#8203;aykutkardas](https://togithub.com/aykutkardas) made their
first contribution in
[https://github.com/pmndrs/zustand/pull/1993](https://togithub.com/pmndrs/zustand/pull/1993)
- [@&#8203;michelts](https://togithub.com/michelts) made their first
contribution in
[https://github.com/pmndrs/zustand/pull/1997](https://togithub.com/pmndrs/zustand/pull/1997)
- [@&#8203;elusive](https://togithub.com/elusive) made their first
contribution in
[https://github.com/pmndrs/zustand/pull/2001](https://togithub.com/pmndrs/zustand/pull/2001)
- [@&#8203;mayank1513](https://togithub.com/mayank1513) made their first
contribution in
[https://github.com/pmndrs/zustand/pull/2015](https://togithub.com/pmndrs/zustand/pull/2015)
- [@&#8203;fdb](https://togithub.com/fdb) made their first contribution
in
[https://github.com/pmndrs/zustand/pull/2029](https://togithub.com/pmndrs/zustand/pull/2029)
- [@&#8203;tmkx](https://togithub.com/tmkx) made their first
contribution in
[https://github.com/pmndrs/zustand/pull/2032](https://togithub.com/pmndrs/zustand/pull/2032)
- [@&#8203;OshriAsulin](https://togithub.com/OshriAsulin) made their
first contribution in
[https://github.com/pmndrs/zustand/pull/2028](https://togithub.com/pmndrs/zustand/pull/2028)
- [@&#8203;ivanquirino](https://togithub.com/ivanquirino) made their
first contribution in
[https://github.com/pmndrs/zustand/pull/2047](https://togithub.com/pmndrs/zustand/pull/2047)
- [@&#8203;stavkamil](https://togithub.com/stavkamil) made their first
contribution in
[https://github.com/pmndrs/zustand/pull/2071](https://togithub.com/pmndrs/zustand/pull/2071)
- [@&#8203;NaamuKim](https://togithub.com/NaamuKim) made their first
contribution in
[https://github.com/pmndrs/zustand/pull/2088](https://togithub.com/pmndrs/zustand/pull/2088)
- [@&#8203;FaberVitale](https://togithub.com/FaberVitale) made their
first contribution in
[https://github.com/pmndrs/zustand/pull/2090](https://togithub.com/pmndrs/zustand/pull/2090)

**Full Changelog**:
pmndrs/zustand@v4.4.1...v4.4.2

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 4pm every weekday" in timezone
Europe/Paris, Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **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.

---

- [ ] <!-- rebase-check -->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/specfy/specfy).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40Ni4wIiwidXBkYXRlZEluVmVyIjoiMzcuNDYuMCIsInRhcmdldEJyYW5jaCI6ImNob3JlL3Jlbm92YXRlQmFzZUJyYW5jaCJ9-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
rexerwang pushed a commit to rexerwang/min-kit that referenced this pull request Dec 28, 2023
Mercury21th000 pushed a commit to Mercury21th000/zustand that referenced this pull request Nov 17, 2024
* feat: add useShallow

See
- pmndrs/zustand#1937
- pmndrs/zustand#1937 (reply in thread)
- pmndrs/zustand#1937 (reply in thread)

* chore(useShallow): improve unit tests

* chore(useShallow): PR feedback pmndrs/zustand#2090 (comment)

* fix(useShallow): tests not working on test_matrix (cjs, production, CI-MATRIX-NOSKIP)

* chore(useShallow): fix eslint warning issue (unused import)

* refactor(useShallow): simplify tests

* docs(useShallow): add guide

* fix(useShallow): prettier:ci error https://github.com/pmndrs/zustand/actions/runs/6369420511/job/17289749161?pr=2090

* docs(useShallow): update readme

* docs(useShallow): remove obsolete line from readme

Co-authored-by: Daishi Kato <[email protected]>

* doc(useShallow): PR feedback pmndrs/zustand#2090 (comment)

* docs(useShallow): small improvements of the useShallow guide

---------

Co-authored-by: Daishi Kato <[email protected]>
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