mirror of
https://github.com/pmndrs/zustand.git
synced 2025-12-08 19:45:52 +00:00
Compare commits
7 Commits
39a391b6c1
...
32a3698e95
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32a3698e95 | ||
|
|
350d9ec6bc | ||
|
|
2cfb6a72d2 | ||
|
|
ddfc158f87 | ||
|
|
f99902226a | ||
|
|
82806501c4 | ||
|
|
957bf89509 |
2
.github/workflows/compressed-size.yml
vendored
2
.github/workflows/compressed-size.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
||||
compressed_size:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
|
||||
2
.github/workflows/preview-release.yml
vendored
2
.github/workflows/preview-release.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
||||
preview_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
|
||||
8
.github/workflows/publish.yml
vendored
8
.github/workflows/publish.yml
vendored
@ -4,11 +4,15 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
@ -18,6 +22,4 @@ jobs:
|
||||
- run: pnpm install
|
||||
- run: pnpm run build
|
||||
- run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
working-directory: dist
|
||||
|
||||
4
.github/workflows/test-multiple-builds.yml
vendored
4
.github/workflows/test-multiple-builds.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
||||
build: [cjs, esm]
|
||||
env: [development] # [development, production]
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
@ -41,7 +41,7 @@ jobs:
|
||||
if: ${{ matrix.build == 'esm' }}
|
||||
run: |
|
||||
sed -i~ "s/resolve('\.\/src\(.*\)\.ts')/resolve('\.\/dist\/esm\1.mjs')/" vitest.config.mts
|
||||
sed -i~ "1s/^/import.meta.env=import.meta.env||{};import.meta.env.MODE='${NODE_ENV}';/" tests/*.tsx
|
||||
sed -i~ "1s/^/import.meta.env.MODE='${NODE_ENV}';/" tests/*.tsx
|
||||
env:
|
||||
NODE_ENV: ${{ matrix.env }}
|
||||
- name: Test ${{ matrix.build }} ${{ matrix.env }}
|
||||
|
||||
2
.github/workflows/test-multiple-versions.yml
vendored
2
.github/workflows/test-multiple-versions.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
- 19.2.0-canary-0bdb9206-20250818
|
||||
- 0.0.0-experimental-0bdb9206-20250818
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
|
||||
9
.github/workflows/test-old-typescript.yml
vendored
9
.github/workflows/test-old-typescript.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
typescript:
|
||||
- 5.9.2
|
||||
- 5.9.3
|
||||
- 5.8.3
|
||||
- 5.7.3
|
||||
- 5.6.3
|
||||
@ -29,7 +29,7 @@ jobs:
|
||||
- 4.6.4
|
||||
- 4.5.5
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
@ -45,14 +45,19 @@ jobs:
|
||||
run: |
|
||||
sed -i~ 's/"verbatimModuleSyntax": true,//' tsconfig.json
|
||||
- name: Patch for Old TS
|
||||
if: ${{ matrix.typescript == '5.3.3' || matrix.typescript == '5.2.2' || matrix.typescript == '5.1.6' || matrix.typescript == '5.0.4' || matrix.typescript == '4.9.5' || matrix.typescript == '4.8.4' || matrix.typescript == '4.7.4' || matrix.typescript == '4.6.4' || matrix.typescript == '4.5.5' }}
|
||||
run: |
|
||||
sed -i~ 's/"moduleResolution": "bundler",/"moduleResolution": "node",/' tsconfig.json
|
||||
sed -i~ 's/"allowImportingTsExtensions": true,//' tsconfig.json
|
||||
sed -i~ 's/"zustand": \["\.\/src\/index\.ts"\],/"zustand": [".\/dist\/index.d.ts"],/' tsconfig.json
|
||||
sed -i~ 's/"zustand\/\*": \["\.\/src\/\*\.ts"\]/"zustand\/*": [".\/dist\/*.d.ts"]/' tsconfig.json
|
||||
sed -i~ 's/"include": .*/"include": ["src\/types.d.ts", "dist\/**\/*", "tests\/**\/*"],/' tsconfig.json
|
||||
- name: Patch for Older TS
|
||||
if: ${{ matrix.typescript == '4.7.4' || matrix.typescript == '4.6.4' || matrix.typescript == '4.5.5' }}
|
||||
run: |
|
||||
pnpm json -I -f package.json -e "this.resolutions={}; this.resolutions['@types/node']='18.13.0';"
|
||||
pnpm add -D @types/node@18.13.0
|
||||
pnpm add -D vitest@3.2.4 @vitest/coverage-v8@3.2.4 @vitest/ui@3.2.4
|
||||
- name: Install old TypeScript
|
||||
run: pnpm add -D typescript@${{ matrix.typescript }}
|
||||
- name: Test ${{ matrix.typescript }}
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
|
||||
@ -8,6 +8,12 @@
|
||||
[](https://www.npmjs.com/package/zustand)
|
||||
[](https://discord.gg/poimandres)
|
||||
|
||||
<a href="https://dai-shi.github.io/zustand-banner-sponsorship/sponsors/" target="_blank" rel="noopener">
|
||||
<p align="center">
|
||||
<img src="https://dai-shi.github.io/zustand-banner-sponsorship/api/banner.png" />
|
||||
</p>
|
||||
</a>
|
||||
|
||||
A small, fast and scalable bearbones state-management solution using simplified flux principles. Has a comfy API based on hooks, isn't boilerplatey or opinionated.
|
||||
|
||||
Don't disregard it because it's cute. It has quite the claws, lots of time was spent dealing with common pitfalls, like the dreaded [zombie child problem](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children), [react concurrency](https://github.com/bvaughn/rfcs/blob/useMutableSource/text/0000-use-mutable-source.md), and [context loss](https://github.com/facebook/react/issues/13332) between mixed renderers. It may be the one state-manager in the React space that gets all of these right.
|
||||
|
||||
@ -6,18 +6,17 @@ export default function Details() {
|
||||
<a href="https://github.com/pmndrs/zustand">Github</a>
|
||||
</nav>
|
||||
<div className="bottom">
|
||||
<a
|
||||
href="https://github.com/pmndrs/zustand/tree/main/examples/demo"
|
||||
className="bottom-right"
|
||||
>
|
||||
{'<Source />'}
|
||||
</a>
|
||||
<a
|
||||
href="https://www.instagram.com/tina.henschel/"
|
||||
className="bottom-left"
|
||||
>
|
||||
<a href="https://www.instagram.com/tina.henschel/">
|
||||
Illustrations @ Tina Henschel
|
||||
</a>
|
||||
<div className="bottom-links">
|
||||
<a href="https://github.com/pmndrs/zustand/tree/main/examples/demo">
|
||||
{'<Source />'}
|
||||
</a>
|
||||
<a href="https://stackblitz.com/github/pmndrs/zustand/tree/main/examples/starter?file=src%2Findex.tsx">
|
||||
{'<Playground />'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<span className="header-left">Zustand</span>
|
||||
</>
|
||||
|
||||
@ -158,14 +158,26 @@ a {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
a.bottom-left {
|
||||
.bottom {
|
||||
position: fixed;
|
||||
bottom: 40px;
|
||||
left: 40px;
|
||||
right: 40px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
a.bottom-right {
|
||||
bottom: 40px;
|
||||
right: 40px;
|
||||
.bottom a {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.bottom-links {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.snippet-container {
|
||||
|
||||
34
package.json
34
package.json
@ -3,7 +3,7 @@
|
||||
"description": "🐻 Bear necessities for state management in React",
|
||||
"private": true,
|
||||
"type": "commonjs",
|
||||
"version": "5.0.8",
|
||||
"version": "5.0.9",
|
||||
"main": "./index.js",
|
||||
"types": "./index.d.ts",
|
||||
"typesVersions": {
|
||||
@ -116,7 +116,7 @@
|
||||
"homepage": "https://github.com/pmndrs/zustand",
|
||||
"packageManager": "pnpm@10.18.3",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.38.0",
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@redux-devtools/extension": "^3.3.0",
|
||||
"@rollup/plugin-alias": "^6.0.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.3",
|
||||
@ -124,37 +124,37 @@
|
||||
"@rollup/plugin-typescript": "12.3.0",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/node": "^24.9.2",
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/use-sync-external-store": "^1.5.0",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"@vitest/eslint-plugin": "^1.3.26",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"esbuild": "^0.25.11",
|
||||
"eslint": "9.38.0",
|
||||
"@vitest/coverage-v8": "^4.0.14",
|
||||
"@vitest/eslint-plugin": "^1.5.0",
|
||||
"@vitest/ui": "^4.0.14",
|
||||
"esbuild": "^0.27.0",
|
||||
"eslint": "9.39.1",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jest-dom": "^5.5.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"eslint-plugin-testing-library": "^7.13.3",
|
||||
"immer": "^10.2.0",
|
||||
"jsdom": "^27.0.1",
|
||||
"eslint-plugin-testing-library": "^7.13.5",
|
||||
"immer": "^11.0.1",
|
||||
"jsdom": "^27.2.0",
|
||||
"json": "^11.0.0",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier": "^3.7.2",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"redux": "^5.0.1",
|
||||
"rollup": "^4.52.5",
|
||||
"rollup": "^4.53.3",
|
||||
"rollup-plugin-esbuild": "^6.2.1",
|
||||
"shelljs": "^0.10.0",
|
||||
"shx": "^0.4.0",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.2",
|
||||
"typescript-eslint": "^8.48.0",
|
||||
"use-sync-external-store": "^1.6.0",
|
||||
"vitest": "^3.2.4"
|
||||
"vitest": "^4.0.14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=18.0.0",
|
||||
|
||||
1699
pnpm-lock.yaml
generated
1699
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -14,3 +14,4 @@ export {
|
||||
type PersistStorage,
|
||||
type PersistOptions,
|
||||
} from './middleware/persist.ts'
|
||||
export { ssrSafe as unstable_ssrSafe } from './middleware/ssrSafe.ts'
|
||||
|
||||
25
src/middleware/ssrSafe.ts
Normal file
25
src/middleware/ssrSafe.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import type { StateCreator, StoreMutatorIdentifier } from '../vanilla.ts'
|
||||
|
||||
// This is experimental middleware. It will be changed before finalizing it.
|
||||
// https://github.com/pmndrs/zustand/discussions/2740
|
||||
// TODO Not very happy with the middleware name. Will revisit it later.
|
||||
export function ssrSafe<
|
||||
T extends object,
|
||||
U extends object,
|
||||
Mps extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
|
||||
>(
|
||||
config: StateCreator<T, Mps, Mcs, U>,
|
||||
isSSR: boolean = typeof window === 'undefined',
|
||||
): StateCreator<T, Mps, Mcs, U> {
|
||||
return (set, get, api) => {
|
||||
if (!isSSR) {
|
||||
return config(set, get, api)
|
||||
}
|
||||
const ssrSet = () => {
|
||||
throw new Error('Cannot set state of Zustand store in SSR')
|
||||
}
|
||||
api.setState = ssrSet
|
||||
return config(ssrSet as never, get, api)
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@ type TupleOfEqualLength<Arr extends unknown[], T> = number extends Arr['length']
|
||||
type Connection = {
|
||||
subscribers: ((message: unknown) => void)[]
|
||||
api: {
|
||||
subscribe: Mock<any>
|
||||
subscribe: Mock<(f: (message: unknown) => void) => () => void>
|
||||
unsubscribe: Mock<any>
|
||||
send: Mock<any>
|
||||
init: Mock<any>
|
||||
@ -93,7 +93,7 @@ const extensionConnector = {
|
||||
? unnamedConnections
|
||||
: namedConnections
|
||||
const subscribers: Connection['subscribers'] = []
|
||||
const api = {
|
||||
const api: Connection['api'] = {
|
||||
subscribe: vi.fn((f: (m: unknown) => void) => {
|
||||
subscribers.push(f)
|
||||
return () => {}
|
||||
|
||||
@ -185,14 +185,16 @@ it('state is covariant', () => {
|
||||
foo: '',
|
||||
}))
|
||||
|
||||
const _testIsCovariant: StoreApi<{ count: number }> = store
|
||||
const testIsCovariant: StoreApi<{ count: number }> = store
|
||||
expect(testIsCovariant).toBeDefined()
|
||||
|
||||
// @ts-expect-error should not compile
|
||||
const _testIsNotContravariant: StoreApi<{
|
||||
const testIsNotContravariant: StoreApi<{
|
||||
count: number
|
||||
foo: string
|
||||
baz: string
|
||||
}> = store
|
||||
expect(testIsNotContravariant).toBeDefined()
|
||||
})
|
||||
|
||||
it('StateCreator<T, [StoreMutatorIdentfier, unknown][]> is StateCreator<T, []>', () => {
|
||||
@ -229,8 +231,9 @@ it('StateCreator subtyping', () => {
|
||||
|
||||
create<State>()(persist(foo(), { name: 'prefix' }))
|
||||
|
||||
const _testSubtyping: StateCreator<State, [['zustand/persist', unknown]]> =
|
||||
const testSubtyping: StateCreator<State, [['zustand/persist', unknown]]> =
|
||||
{} as StateCreator<State, []>
|
||||
expect(testSubtyping).toBeDefined()
|
||||
})
|
||||
|
||||
it('set state exists on store with readonly store', () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user