Repository: pmndrs/valtio
Branch: main
Commit: 383ccf185997
Files: 203
Total size: 429.7 KB
Directory structure:
gitextract_vdkwfl3k/
├── .codesandbox/
│ └── ci.json
├── .github/
│ ├── DISCUSSION_TEMPLATE/
│ │ └── bug-report.yml
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── config.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── compressed-size.yml
│ ├── preview-release.yml
│ ├── publish.yml
│ ├── test-multiple-builds.yml
│ ├── test-multiple-versions.yml
│ ├── test-old-typescript.yml
│ └── test.yml
├── .gitignore
├── .prettierignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs/
│ ├── api/
│ │ ├── advanced/
│ │ │ ├── ref.mdx
│ │ │ ├── snapshot.mdx
│ │ │ ├── subscribe-ops.mdx
│ │ │ └── subscribe.mdx
│ │ ├── basic/
│ │ │ ├── proxy.mdx
│ │ │ └── useSnapshot.mdx
│ │ ├── hacks/
│ │ │ ├── getVersion.mdx
│ │ │ └── internals.mdx
│ │ └── utils/
│ │ ├── derive.mdx
│ │ ├── devtools.mdx
│ │ ├── proxyMap.mdx
│ │ ├── proxySet.mdx
│ │ ├── proxyWithHistory.mdx
│ │ ├── subscribeKey.mdx
│ │ ├── unstable_deepProxy.mdx
│ │ └── watch.mdx
│ ├── guides/
│ │ ├── async.mdx
│ │ ├── component-state.mdx
│ │ ├── computed-properties.mdx
│ │ └── migrating-to-v2.mdx
│ ├── how-tos/
│ │ ├── how-to-avoid-rerenders-manually.mdx
│ │ ├── how-to-easily-access-the-state-from-anywhere-in-the-application.mdx
│ │ ├── how-to-organize-actions.mdx
│ │ ├── how-to-persist-states.mdx
│ │ ├── how-to-reset-state.mdx
│ │ ├── how-to-split-and-compose-states.mdx
│ │ ├── how-to-update-values-inside-arrays.mdx
│ │ ├── how-to-use-with-context.mdx
│ │ ├── how-valtio-works.mdx
│ │ └── some-gotchas.mdx
│ ├── introduction/
│ │ └── getting-started.mdx
│ ├── introduction.mdx
│ ├── readme.md
│ └── resources/
│ ├── community.mdx
│ ├── learn.mdx
│ └── libraries.mdx
├── eslint.config.mjs
├── examples/
│ ├── README.md
│ ├── counter/
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ ├── prism.css
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── editor-proxyWithHistory/
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── photo-booth-vanillajs/
│ │ ├── index.html
│ │ ├── package.json
│ │ └── src/
│ │ ├── index.css
│ │ └── main.js
│ ├── starter/
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.css
│ │ │ ├── index.tsx
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── subscribe/
│ │ └── index.html
│ ├── todo/
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ ├── prism.css
│ │ │ ├── store.ts
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ └── todo-with-proxyMap/
│ ├── index.html
│ ├── package.json
│ ├── src/
│ │ ├── AddTodoInput.tsx
│ │ ├── App.tsx
│ │ ├── Filter.tsx
│ │ ├── TodoItem.tsx
│ │ ├── TodoList.tsx
│ │ ├── main.tsx
│ │ ├── react-app-env.d.ts
│ │ ├── store.ts
│ │ └── styles.css
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
├── package.json
├── pnpm-workspace.yaml
├── rollup.config.mjs
├── src/
│ ├── index.ts
│ ├── react/
│ │ ├── utils/
│ │ │ └── useProxy.ts
│ │ └── utils.ts
│ ├── react.ts
│ ├── types.d.ts
│ ├── utils.ts
│ ├── vanilla/
│ │ ├── utils/
│ │ │ ├── deepClone.ts
│ │ │ ├── deepProxy.ts
│ │ │ ├── devtools.ts
│ │ │ ├── proxyMap.ts
│ │ │ ├── proxySet.ts
│ │ │ ├── subscribeKey.ts
│ │ │ └── watch.ts
│ │ └── utils.ts
│ └── vanilla.ts
├── tests/
│ ├── async.test.tsx
│ ├── basic.test.tsx
│ ├── class.test.tsx
│ ├── deepClone.test.tsx
│ ├── deepProxy.test.tsx
│ ├── devtools.test.tsx
│ ├── getter.test.tsx
│ ├── mapset.test.tsx
│ ├── memoryleaks.test.ts
│ ├── optimization.test.tsx
│ ├── performance.test.tsx
│ ├── proxyMap.bench.ts
│ ├── proxyMap.test.tsx
│ ├── proxySet.test.tsx
│ ├── ref.test.tsx
│ ├── setup.ts
│ ├── snapshot.test.ts
│ ├── subscribe.test.tsx
│ ├── useProxy.test.tsx
│ ├── utils.tsx
│ └── watch.test.tsx
├── tsconfig.json
├── vitest.config.mts
└── website/
├── .eslintrc.json
├── .gitignore
├── README.md
├── _utils/
│ ├── file_helpers.ts
│ └── index.ts
├── components/
│ ├── LandingPage/
│ │ ├── AnimatedShapes.tsx
│ │ ├── CodeExample.tsx
│ │ ├── GettingStarted.tsx
│ │ ├── state.ts
│ │ └── useFloatAnimation.tsx
│ ├── MDXRenderer/
│ │ ├── MDXRenderer.tsx
│ │ └── index.ts
│ ├── SEO/
│ │ ├── SEO.tsx
│ │ └── index.ts
│ ├── ToggleTheme/
│ │ ├── ToggleTheme.tsx
│ │ └── index.ts
│ └── layouts/
│ ├── BasicLayout/
│ │ ├── BasicLayout.tsx
│ │ └── index.ts
│ ├── DocLayout/
│ │ ├── DocLayout.tsx
│ │ └── index.ts
│ ├── Header/
│ │ ├── Header.tsx
│ │ └── index.ts
│ └── index.ts
├── hooks/
│ ├── index.ts
│ ├── useCodesandboxTheme.ts
│ ├── useIsomorphicLayoutEffect.ts
│ └── useTheme.ts
├── lib/
│ ├── mdx.ts
│ └── remarkCodeSandboxURLUpdater.ts
├── next-env.d.ts
├── next.config.js
├── package.json
├── pages/
│ ├── _app.tsx
│ ├── _document.tsx
│ ├── docs/
│ │ └── [...slug].tsx
│ └── index.tsx
├── postcss.config.js
├── state/
│ ├── index.ts
│ └── useThemeState.ts
├── styles/
│ ├── landing-page.css
│ ├── prism-theme.css
│ └── tailwind.css
├── tailwind.config.js
├── tsconfig.json
└── types.d.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .codesandbox/ci.json
================================================
{
"packages": ["dist"],
"sandboxes": [
"new",
"react-typescript-react-ts",
"simple-react-browserify-x9yni",
"simple-snowpack-react-o1gmx",
"react-parcel-onewf"
],
"node": "18"
}
================================================
FILE: .github/DISCUSSION_TEMPLATE/bug-report.yml
================================================
labels: ['bug']
body:
- type: markdown
attributes:
value: If you don't have a reproduction link, please choose a different category.
- type: textarea
attributes:
label: Bug Description
description: Describe the bug you encountered
validations:
required: true
- type: input
attributes:
label: Reproduction Link
description: A link to a [TypeScript Playground](https://www.typescriptlang.org/play), a [StackBlitz Project](https://stackblitz.com/) or something else with a minimal reproduction.
validations:
required: true
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [dai-shi] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: ['https://daishi.gumroad.com/l/learn-valtio'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Assigned issue
about: This is to create a new issue that already has an assignee. Please open a new discussion otherwise.
title: ''
labels: ''
assignees: ''
---
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Bug Reports
url: https://github.com/pmndrs/valtio/discussions/new?category=bug-report
about: Please post bug reports here.
- name: Questions
url: https://github.com/pmndrs/valtio/discussions/new?category=q-a
about: Please post questions here.
- name: Other Discussions
url: https://github.com/pmndrs/valtio/discussions/new/choose
about: Please post ideas and general discussions here.
================================================
FILE: .github/pull_request_template.md
================================================
## Related Bug Reports or Discussions
Fixes #
## Summary
## Check List
- [ ] `pnpm run fix` for formatting and linting code and docs
================================================
FILE: .github/workflows/compressed-size.yml
================================================
name: Compressed Size
on: [pull_request]
jobs:
compressed_size:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 'lts/*'
cache: 'pnpm'
- uses: preactjs/compressed-size-action@49c7ff02f46adc39a83c24e91f6110ba8138a19d # v3
with:
pattern: './dist/**/*.{js,mjs}'
================================================
FILE: .github/workflows/preview-release.yml
================================================
name: Preview Release
on: [push, pull_request]
jobs:
preview_release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 'lts/*'
cache: 'pnpm'
- run: pnpm install
- run: pnpm run build
- run: pnpm dlx pkg-pr-new publish './dist' --compact --template './examples/*'
================================================
FILE: .github/workflows/publish.yml
================================================
name: Publish
on:
release:
types: [published]
permissions:
id-token: write
contents: read
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 'lts/*'
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
- run: pnpm install
- run: pnpm run build
- run: npm publish
working-directory: dist
================================================
FILE: .github/workflows/test-multiple-builds.yml
================================================
name: Test Multiple Builds
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize]
jobs:
test_multiple_builds:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
build: [cjs, esm]
env: [development] # [development, production]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 'lts/*'
cache: 'pnpm'
- run: pnpm install
- run: pnpm run build
- name: Patch for DEV-ONLY
if: ${{ matrix.env == 'development' }}
run: |
sed -i~ "s/it[.a-zA-Z]*('\[DEV-ONLY\]/it('/" tests/*.ts tests/*.tsx
sed -i~ "s/it[.a-zA-Z]*('\[PRD-ONLY\]/it.skip('/" tests/*.ts tests/*.tsx
- name: Patch for PRD-ONLY
if: ${{ matrix.env == 'production' }}
run: |
sed -i~ "s/it[.a-zA-Z]*('\[PRD-ONLY\]/it('/" tests/*.ts tests/*.tsx
sed -i~ "s/it[.a-zA-Z]*('\[DEV-ONLY\]/it.skip('/" tests/*.ts tests/*.tsx
- name: Patch for CJS
if: ${{ matrix.build == 'cjs' }}
run: |
sed -i~ "s/resolve('\.\/src\(.*\)\.ts')/resolve('\.\/dist\1.js')/" vitest.config.mts
- name: Patch for ESM
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.MODE='${NODE_ENV}';/" tests/*.ts tests/*.tsx
env:
NODE_ENV: ${{ matrix.env }}
- name: Test ${{ matrix.build }} ${{ matrix.env }}
run: |
pnpm run test:spec
env:
NODE_ENV: ${{ matrix.env }}
================================================
FILE: .github/workflows/test-multiple-versions.yml
================================================
name: Test Multiple Versions
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize]
jobs:
test_multiple_versions:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
react:
- 18.0.0
- 18.1.0
- 18.2.0
- 18.3.1
- 19.0.0
- 19.1.0
- 19.2.0
- 19.3.0-canary-e0cc7202-20260227
- 0.0.0-experimental-e0cc7202-20260227
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 'lts/*'
cache: 'pnpm'
- run: pnpm install
- name: Install legacy testing-library
if: ${{ startsWith(matrix.react, '16.') || startsWith(matrix.react, '17.') }}
run: pnpm add -D @testing-library/react@12.1.4
- name: Patch for React 16
if: ${{ startsWith(matrix.react, '16.') }}
run: |
sed -i~ '1s/^/import React from "react";/' tests/*.tsx
sed -i~ 's/"jsx": "react-jsx"/"jsx": "react"/' tsconfig.json
sed -i~ 's/import\.meta\.env[?]\.MODE/"DEVELOPMENT".toLowerCase()/' src/*.ts src/*/*.ts src/*/*/*.ts
- name: Test ${{ matrix.react }}
run: |
pnpm add -D react@${{ matrix.react }} react-dom@${{ matrix.react }}
pnpm run test:spec
================================================
FILE: .github/workflows/test-old-typescript.yml
================================================
name: Test Old TypeScript
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize]
jobs:
test_old_typescript:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
typescript:
- 5.9.3
- 5.8.3
- 5.7.3
- 5.6.3
- 5.5.4
- 5.4.5
- 5.3.3
- 5.2.2
- 5.1.6
- 5.0.4
- 4.9.5
- 4.8.4
- 4.7.4
- 4.6.4
- 4.5.5
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 'lts/*'
cache: 'pnpm'
- run: pnpm install
- run: pnpm run build
- name: Patch for all TS
run: |
sed -i~ 's/"isolatedDeclarations": true,//' tsconfig.json
- name: Patch for v4/v3 TS
if: ${{ startsWith(matrix.typescript, '4.') || startsWith(matrix.typescript, '3.') }}
run: |
sed -i~ 's/"verbatimModuleSyntax": true,//' tsconfig.json
- name: Patch for Old TS
if: ${{ matrix.typescript == '5.5.4' || matrix.typescript == '5.4.5' || 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/"valtio": \["\.\/src\/index\.ts"\],/"valtio": [".\/dist\/index.d.ts"],/' tsconfig.json
sed -i~ 's/"valtio\/\*": \["\.\/src\/\*\.ts"\]/"valtio\/*": [".\/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 }}
run: pnpm run test:types
================================================
FILE: .github/workflows/test.yml
================================================
name: Test
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version: 'lts/*'
cache: 'pnpm'
- run: pnpm install
- run: pnpm run test:format
- run: pnpm run test:types
- run: pnpm run test:lint
- run: pnpm run test:spec
- run: pnpm run build # we don't have any other workflows to test build
================================================
FILE: .gitignore
================================================
# dependencies
node_modules
.pnp
.pnp.js
# testing
coverage
# production
dist
build
# dotenv environment variables file
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# misc
.DS_Store
.idea
# examples
examples/**/*/package-lock.json
examples/**/*/yarn.lock
examples/**/*/pnpm-lock.yaml
examples/**/*/bun.lockb
================================================
FILE: .prettierignore
================================================
dist
pnpm-lock.yaml
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
## General Guideline
### Reporting Issues
If you have found what you think is a bug, please [start a discussion](https://github.com/pmndrs/valtio/discussions/new?category=bug-report).
For any usage questions, please [start a discussion](https://github.com/pmndrs/valtio/discussions/new?category=q-a).
### Suggesting New Features
If you are here to suggest a feature, first [start a discussion](https://github.com/pmndrs/valtio/discussions/new?category=ideas) if it does not already exist. From there, we will discuss use-cases for the feature and then finally discuss how it could be implemented.
### Committing
We are applying [conventional commit spec](https://www.conventionalcommits.org/en/v1.0.0/) here. In short, that means a commit has to be one of the following types:
Your commit type must be one of the following:
- **feat**: A new feature.
- **fix**: A bug fix.
- **refactor**: A code change that neither fixes a bug nor adds a feature.
- **chore**: Changes to the build process, configuration, dependencies, CI/CD pipelines, or other auxiliary tools and libraries.
- **docs**: Documentation-only changes.
- **test**: Adding missing or correcting existing tests.
If you are unfamiliar with the usage of conventional commits,
the short version is to simply specify the type as a first word,
and follow it with a colon and a space, then start your message
from a lowercase letter, like this:
```
feat: add a 'foo' type support
```
You can also specify the scope of the commit in the parentheses after a type:
```
fix(react): change the 'bar' parameter type
```
### Development
If you would like to contribute by fixing an open issue or developing a new feature you can use this suggested workflow:
#### General
1. Fork this repository.
2. Create a new feature branch based off the `main` branch.
3. Follow the [Core](#Core) and/or the [Documentation](#Documentation) guide below and come back to this once done.
4. Run `pnpm run fix:format` to format the code.
5. Git stage your required changes and commit (review the commit guidelines below).
6. Submit the PR for review.
##### Core
1. Run `pnpm install` to install dependencies.
2. Create failing tests for your fix or new feature in the [`tests`](./tests/) folder.
3. Implement your changes.
4. Run `pnpm run build` to build the library. _(Pro-tip: `pnpm run build-watch` runs the build in watch mode)_
5. Run the tests by running `pnpm run test` and ensure that they pass.
6. You can use `pnpm link` to sym-link this package and test it locally on your own project. Alternatively, you may use CodeSandbox CI's canary releases to test the changes in your own project. (requires a PR to be created first)
7. Follow step 4 and onwards from the [General](#General) guide above to bring it to the finish line.
### Pull Requests
Please try to keep your pull request focused in scope and avoid including unrelated commits.
After you have submitted your pull request, we'll try to get back to you as soon as possible. We may suggest some changes or request improvements, therefore, please check ✅ ["Allow edits from maintainers"](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) on your PR.
## Valtio-specific Guideline
##### Documentation
1. Navigate to the [`website`](./website/) folder. (e.g., `cd website`).
2. Run `pnpm install` to install dependencies in the `website` folder.
3. Run `pnpm run dev` to start the dev server.
4. Navigate to [`http://localhost:3000`](http://localhost:3000) to view the documents.
5. Navigate to the [`docs`](./docs/) folder and make necessary changes to the documents.
6. Add your changes to the documents and see them live reloaded in the browser.
7. Follow step 4 and onwards from the [General](#General) guide above to bring it to the finish line.
Thank you for contributing! :heart:
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Poimandres
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
npm install valtio makes proxy-state simple
[](https://github.com/pmndrs/valtio/actions/workflows/test.yml?query=branch%3Amain)
[](https://bundlephobia.com/result?p=valtio)
[](https://www.npmjs.com/package/valtio)
[](https://www.npmjs.com/package/valtio)
[](https://discord.gg/poimandres)
#### Wrap your state object
Valtio turns the object you pass it into a self-aware proxy.
```jsx
import { proxy, useSnapshot } from 'valtio'
const state = proxy({ count: 0, text: 'hello' })
```
#### Mutate from anywhere
You can make changes to it in the same way you would to a normal js-object.
```jsx
setInterval(() => {
++state.count
}, 1000)
```
#### React via useSnapshot
Create a local snapshot that catches changes. Rule of thumb: read from snapshots in render function, otherwise use the source. The component will only re-render when the parts of the state you access have changed, it is render-optimized.
```jsx
// This will re-render on `state.count` change but not on `state.text` change
function Counter() {
const snap = useSnapshot(state)
return (
this is for expert users.
💡 Tip If you are using valtio outside of react, import from `valtio/vanilla` ```js import { proxy, snapshot } from 'valtio/vanilla' ```================================================ FILE: docs/api/advanced/subscribe-ops.mdx ================================================ --- title: 'subscribe-ops' section: 'API' subSection: 'Advanced' description: 'Fine-grained mutation tracking with operational transformations (Ops)' --- # Subscribe Ops By default, Valtio's `subscribe` notify you that _something_ has changed in the state proxy. However, you can opt-in to receiving **Ops** (Operations), which are detailed descriptions of exactly what was modified. ## What are Ops? Ops are granular mutation records. When a proxy is updated, Valtio can generate a description of the change as a tuple. ### Op Types - **`set`**: `[op: 'set', path: Path, value: unknown, prevValue: unknown]` - Triggered when a property is assigned a new value. - **`delete`**: `[op: 'delete', path: Path, prevValue: unknown]` - Triggered when a property is deleted. - **`resolve`**: `[op: 'resolve', path: Path, value: unknown]` - Triggered when a promise in the state is fulfilled. - **`reject`**: `[op: 'reject', path: Path, error: unknown]` - Triggered when a promise in the state is rejected. The `Path` is an array of strings or symbols representing the nested location of the property (e.g., `['user', 'profile', 'name']`). ## How to use Ops The "ops" feature is an **opt-in** feature because it introduces a small performance overhead for tracking and allocating these operation objects. ### 1. Enabling Ops You must explicitly enable op-tracking using the unstable API: ```javascript import { unstable_enableOp } from 'valtio' // Enable globally unstable_enableOp(true) ``` ### 2. Receiving Ops in `subscribe` Once enabled, the `subscribe` callback receives an array of these operations as its first argument. ```javascript import { proxy, subscribe, unstable_enableOp } from 'valtio' unstable_enableOp(true) const state = proxy({ count: 0, text: 'hello' }) subscribe(state, (ops) => { ops.forEach((op) => { const [action, path, value, prevValue] = op console.log(`Action: ${action} at ${path.join('.')}`) console.log(`New value:`, value) console.log(`Previous value:`, prevValue) }) }) state.count++ // Output: // Action: set at count // New value: 1 // Previous value: 0 ``` > **Note**: If `unstable_enableOp(true)` is not called, the `ops` argument will be an empty array or `undefined`. ## Use Cases While standard `subscribe` is sufficient for most React UI updates, Ops are useful for specific advanced scenarios: 1. **Network Synchronization**: Instead of sending the entire state over the wire, you can send only the `ops` (patches). This significantly reduces bandwidth consumption in distributed applications. 2. **Undo/Redo History**: Use the `prevValue` provided in `set` and `delete` ops to easily revert state changes. 3. **Audit Logs & Debugging**: Track a sequence of user-driven mutations for analytics or time-travel debugging. 4. **Devtools Integration**: Powering custom development tools that need to visualize state transitions. ## Performance Considerations Enabling Ops has a small overhead cost. For every mutation, Valtio must: - Detect the change type. - Construct the `Path` array. - Allocate the `Op` tuple. In high-frequency update scenarios (e.g., animations, canvas interactions moving hundreds of objects per frame), this can lead to: - Increased garbage collection (GC) pressure due to object allocations. - A measurable drop in frame rates (FPS). **Recommendation**: Only enable `unstable_enableOp` if your application actually consumes the granular `ops` data. ================================================ FILE: docs/api/advanced/subscribe.mdx ================================================ --- title: 'subscribe' section: 'API' subSection: 'Advanced' description: 'Subscribe to a current state/object' --- # `subscribe` ## Subscribe from anywhere You can access state outside your components and subscribe to changes. ```jsx import { proxy, subscribe } from 'valtio' const state = proxy({ count: 0 }) // Subscribe to all changes to the state proxy (and its child proxies) const unsubscribe = subscribe(state, () => console.log('state has changed to', state), ) // Unsubscribe by calling the result unsubscribe() ``` You can also subscribe to a portion of state. ```jsx const state = proxy({ obj: { foo: 'bar' }, arr: ['hello'] }) subscribe(state.obj, () => console.log('state.obj has changed to', state.obj)) state.obj.foo = 'baz' subscribe(state.arr, () => console.log('state.arr has changed to', state.arr)) state.arr.push('world') ``` ## Codesandbox demo in VanillaJS https://codesandbox.io/s/valtio-photo-booth-demo-forked-xp8hs?file=/src/main.js ## Advanced: Listening to "Ops" The `subscribe` callback can also receive **Ops** (Operations), which are detailed records of exactly what changed (e.g., which property was set or deleted). This is useful for advanced scenarios like synchronization or undo/redo. Because tracking these operations has a small performance cost, they are disabled by default. For more details on how to enable and use them, check out [Subscribe Ops](./subscribe-ops). ================================================ FILE: docs/api/basic/proxy.mdx ================================================ --- title: 'proxy' section: 'API' subSection: 'Basic' description: 'Create a proxy object.' --- # `proxy` The `proxy` tracks changes to the original object and all nested objects, notifying listeners when an object is modified. ```js import { proxy } from 'valtio' const state = proxy({ count: 0, text: 'hello' }) ``` ## Mutate from anywhere You can make changes to it in the same way you would to a normal js-object. ```js setInterval(() => { ++state.count }, 1000) ``` ## Optimizations: noop and batching Updates that set the value of a property to the same value are ignored. Subscribers will not be informed of a version change. ```js const state = proxy({ count: 0 }) state.count = 0 // has no effect ``` Multiple changes in the same event loop tick will be batched together. Subscribers will be notified of a single version change. ```js const state = proxy({ count: 0, text: 'hello' }) // subscribers will be notified once after both mutations state.count = 1 state.text = 'world' ``` ## Nested proxies Proxies can be nested in other `proxy` objects and updated as a whole. ```jsx import { proxy, useSnapshot } from 'valtio' const personState = proxy({ name: 'Timo', role: 'admin' }) const authState = proxy({ status: 'loggedIn', user: personState }) authState.user.name = 'Nina' ``` ## Promises in proxies See [`async`](../../guides/async) for more details. ```jsx import { proxy } from 'valtio' const bombState = proxy({ explosion: new Promise((resolve) => setTimeout(() => resolve('Boom!'), 3000)), }) ``` ## Gotchas If you reassign the proxy to an entirely new object, it will stop working because you are replacing the proxied object with a new object reference. ```jsx let state = proxy({ user: { name: 'Timo' } }) subscribe(state, () => { console.log(state.user.name) }) // will not notify subscribers state = { user: { name: 'Nina' } } // instead let state = proxy({ user: { name: 'Timo' } }) subscribe(state, () => { console.log(state.user.name) // logs "Nina" }) // will notify subscribers state.user.name = 'Nina' ``` Not everything can be proxied. Generally, you are safe if it is serializable. Classes can also be proxied. But avoid special objects. ```jsx // these won't work - changes to these objects won't cause updates // to store state that is unproxied see the docs on ref const state = proxy({ chart: d3.select('#chart'), component: React.createElement('div'), map: new Map(), // see proxyMap storage: localStorage, }) // this will work class User { first = null last = null constructor(first, last) { this.first = first this.last = last } greet() { return `Hi ${this.first}!` } get fullName() { return `${this.first} ${this.last}` } } const state = proxy(new User('Timo', 'Kivinen')) ``` ================================================ FILE: docs/api/basic/useSnapshot.mdx ================================================ --- title: 'useSnapshot' section: 'API' subSection: 'Basic' description: 'Create a local snapshot that catches changes.' --- # `useSnapshot` Create a local `snapshot` that catches changes. Normally, Valtio's snapshots (created via `snapshot()`) are recreated on _any_ change to a proxy, or any of its child proxies. However `useSnapshot` wraps the Valtio snapshot in an access-tracking proxy. This is to make sure your component is render optimized, i.e. it will only re-render if keys that it (or its child components) specifically accessed has changed, and not on every single change to the proxy. ## Usage ### Read from snapshots in render, use the proxy in callbacks Snapshots are read-only to render the JSX from their consistent view of the data. Mutations, and also any reads in callbacks that make mutations, need to be made via the proxy, so that the callback reads & writes the latest value. ```jsx function Counter() { const snap = useSnapshot(state) return (
!! There are two disclaimers to using the debug value 1. Due to the way `useSnapshot` uses a proxy to recorded accesses _after_ `useSnapshot` has returned, the fields listed in `useDebugValue` are technically from the _previous_ render. 2. Object getter and class getter calls are not included in the `useDebugValue` output, but don't worry, they are actually correctly tracked internally and correctly trigger re-renders when changed.
ℹ️ Note Getters in JavaScript are a more advanced feature of the language, so Valtio recommends using them with caution. That said, if you are a more advanced JavaScript programmer, they should work as you expect; see the "Note about using `this`" section below.
ℹ️ Note In the current implementation a computed property should only reference \*\*\_sibling\*\*\* properties, otherwise you'll encounter weird bugs. For example: