Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/pmndrs/valtio
💊 Valtio makes proxy-state simple for React and Vanilla
https://github.com/pmndrs/valtio
easy mutable react state
Last synced: 6 days ago
JSON representation
💊 Valtio makes proxy-state simple for React and Vanilla
- Host: GitHub
- URL: https://github.com/pmndrs/valtio
- Owner: pmndrs
- License: mit
- Created: 2020-11-16T22:30:51.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2024-04-29T02:36:07.000Z (6 months ago)
- Last Synced: 2024-05-01T11:29:10.808Z (6 months ago)
- Topics: easy, mutable, react, state
- Language: TypeScript
- Homepage: http://valtio.pmnd.rs
- Size: 3.55 MB
- Stars: 8,460
- Watchers: 39
- Forks: 231
- Open Issues: 6
-
Metadata Files:
- Readme: readme.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-github-repos - pmndrs/valtio - 🧙 Valtio makes proxy-state simple for React and Vanilla (TypeScript)
- awesome-list - valtio - state simple for React and Vanilla | pmndrs | 2804 | (TypeScript)
- awesome-react-three-fiber - valtio
- awesome-webxr-development - Valtio - badge] - Makes proxy-state simple for React and Vanilla (State Management / Visual Editor)
- awesome-web-dev - valtio
- awesome-react-state-management - valtio - Simple proxy-state. (List)
- awesome-f2e-libs - **valtio**
- awesome-star-libs - pmndrs / valtio
- awesome-fe-resources - valtio
- my-awesome-list - valtio - state simple for React and Vanilla | pmndrs | 9067 | (TypeScript)
README
npm i valtio
makes proxy-state simple[![Build Status](https://img.shields.io/github/actions/workflow/status/pmndrs/valtio/lint-and-type.yml?branch=main&style=flat&colorA=000000&colorB=000000)](https://github.com/pmndrs/valtio/actions?query=workflow%3ALint)
[![Build Size](https://img.shields.io/bundlephobia/minzip/valtio?label=bundle%20size&style=flat&colorA=000000&colorB=000000)](https://bundlephobia.com/result?p=valtio)
[![Version](https://img.shields.io/npm/v/valtio?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/valtio)
[![Downloads](https://img.shields.io/npm/dt/valtio.svg?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/valtio)
[![Discord Shield](https://img.shields.io/discord/740090768164651008?style=flat&colorA=000000&colorB=000000&label=discord&logo=discord&logoColor=ffffff)](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 (
{snap.count}
++state.count}>+1
)
}
```Note for TypeScript users: Return type of useSnapshot can be too strict.
The `snap` variable returned by `useSnapshot` is a (deeply) read-only object.
Its type has `readonly` attribute, which may be too strict for some use cases.To mitigate typing difficulties, you might want to loosen the type definition:
```ts
declare module 'valtio' {
function useSnapshot(p: T): T
}
```See [#327](https://github.com/pmndrs/valtio/issues/327) for more information.
Note: useSnapshot returns a new proxy for render optimization.
Internally, `useSnapshot` calls `snapshot` in valtio/vanilla,
and wraps the snapshot object with another proxy to detect property access.
This feature is based on [proxy-compare](https://github.com/dai-shi/proxy-compare).Two kinds of proxies are used for different purposes:
- `proxy()` from `valtio/vanilla` is for mutation tracking or write tracking.
- `createProxy()` from `proxy-compare` is for usage tracking or read tracking.Use of
this
is for expert users.Valtio tries best to handle `this` behavior
but it's hard to understand without familiarity.```js
const state = proxy({
count: 0,
inc() {
++this.count
},
})
state.inc() // `this` points to `state` and it works fine
const snap = useSnapshot(state)
snap.inc() // `this` points to `snap` and it doesn't work because snapshot is frozen
```To avoid this pitfall, the recommended pattern is not to use `this` and prefer arrow function.
```js
const state = proxy({
count: 0,
inc: () => {
++state.count
},
})
```If you are new to this, it's highly recommended to use
[eslint-plugin-valtio](https://github.com/pmndrs/eslint-plugin-valtio).#### Subscribe from anywhere
You can access state outside of your components and subscribe to changes.
```jsx
import { subscribe } from 'valtio'// Subscribe to all state changes
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')
```To subscribe to a primitive value of state, consider `subscribeKey` in utils.
```jsx
import { subscribeKey } from 'valtio/utils'const state = proxy({ count: 0, text: 'hello' })
subscribeKey(state, 'count', (v) =>
console.log('state.count has changed to', v),
)
```There is another util `watch` which might be convenient in some cases.
```jsx
import { watch } from 'valtio/utils'const state = proxy({ count: 0 })
const stop = watch((get) => {
console.log('state has changed to', get(state)) // auto-subscribe on use
})
```#### Suspend your components
Valtio is compatible with React 19 `use` hook. This eliminates all the async back-and-forth, you can access your data directly while the parent is responsible for fallback state and error handling.
```jsx
import { use } from 'react' // React 19
// import { use } from 'react18-use' // React 18const state = proxy({ post: fetch(url).then((res) => res.json()) })
function Post() {
const snap = useSnapshot(state)
return{use(snap.post).title}
}function App() {
return (
waiting...}>
)
}
```It still suffers from "de-opt", which prevents `useTransition` to work well. To mitigate it, there is a third-party library [use-valtio](https://github.com/valtiojs/use-valtio).
#### Holding objects in state without tracking them
This may be useful if you have large, nested objects with accessors that you don't want to proxy. `ref` allows you to keep these objects inside the state model.
See [#61](https://github.com/pmndrs/valtio/issues/61) and [#178](https://github.com/pmndrs/valtio/issues/178) for more information.
```js
import { proxy, ref } from 'valtio'const state = proxy({
count: 0,
dom: ref(document.body),
})
```#### Update transiently (for often occurring state-changes)
You can read state in a component without causing re-render.
```jsx
function Foo() {
const { count, text } = state
// ...
```Or, you can have more control with subscribing in useEffect.
```jsx
function Foo() {
const total = useRef(0)
useEffect(() => subscribe(state.arr, () => {
total.current = state.arr.reduce((p, c) => p + c)
}), [])
// ...
```#### Update synchronously
By default, state mutations are batched before triggering re-render.
Sometimes, we want to disable the batching.
The known use case of this is `` [#270](https://github.com/pmndrs/valtio/issues/270).```jsx
function TextBox() {
const snap = useSnapshot(state, { sync: true })
return (
(state.text = e.target.value)} />
)
}
```#### Dev tools
You can use [Redux DevTools Extension](https://github.com/reduxjs/redux-devtools) for plain objects and arrays.
```jsx
import { devtools } from 'valtio/utils'const state = proxy({ count: 0, text: 'hello' })
const unsub = devtools(state, { name: 'state name', enabled: true })
```Manipulating state with Redux DevTools
The screenshot below shows how to use Redux DevTools to manipulate state. First select the object from the instances drop down. Then type in a JSON object to dispatch. Then click "Dispatch". Notice how it changes the state.
#### Use it vanilla
Valtio is not tied to React, you can use it in vanilla-js.
```jsx
import { proxy, subscribe, snapshot } from 'valtio/vanilla'
// import { ... } from 'valtio/vanilla/utils'const state = proxy({ count: 0, text: 'hello' })
subscribe(state, () => {
console.log('state is mutated')
const obj = snapshot(state) // A snapshot is an immutable object
})
```#### `useProxy` util
While the separation of proxy state and its snapshot is important,
it's confusing for beginners.
We have a convenient util to improve developer experience. useProxy returns shallow proxy state and its snapshot, meaning you can only mutate on root level.```js
import { useProxy } from 'valtio/utils'const state = proxy({ count: 1 })
const Component = () => {
// useProxy returns a special proxy that can be used both in render and callbacks
// The special proxy has to be used directly in a function scope. You can't destructure it outside the scope.
const $state = useProxy(state)
return (
{$state.count}
++$state.count}>+1
)
}
```#### Computed properties
You can define computed properties with object getters.
```js
const state = proxy({
count: 1,
get doubled() {
return this.count * 2
},
})
```Consider it as an advanced usage, because the behavior of `this` is sometimes confusing.
For more information, check out [this guide](./docs/guides/computed-properties.mdx).
#### `proxySet` util
This is to create a proxy which mimic the native Set behavior. The API is the same as Set API
```js
import { proxySet } from 'valtio/utils'const state = proxySet([1, 2, 3])
//can be used inside a proxy as well
//const state = proxy({
// count: 1,
// set: proxySet()
//})state.add(4)
state.delete(1)
state.forEach((v) => console.log(v)) // 2,3,4
```#### `proxyMap` util
This is to create a proxy which emulate the native Map behavior. The API is the same as Map API
```js
import { proxyMap } from 'valtio/utils'const state = proxyMap([
['key', 'value'],
['key2', 'value2'],
])
state.set('key', 'value')
state.delete('key')
state.get('key') // ---> value
state.forEach((value, key) => console.log(key, value)) // ---> "key", "value", "key2", "value2"
```#### Compatibility
Valtio v2 works with React 18 and up.
It only depends on `react` and works with any
renderers such as `react-dom`, `react-native`, `react-three-fiber`, and so on.Valtio works on Node.js, Next.js and other frameworks.
Valtio also works without React. See [vanilla](#use-it-vanilla).
#### Plugins
- [eslint-plugin-valtio](https://github.com/pmndrs/eslint-plugin-valtio)
#### Recipes
Valtio is unopinionated about best practices.
The community is working on recipes.- [How to organize actions](https://github.com/pmndrs/valtio/blob/main/docs/how-tos/how-to-organize-actions.mdx)
- [How to persist states](https://github.com/pmndrs/valtio/blob/main/docs/how-tos/how-to-persist-states.mdx)
- [How to use with context](https://github.com/pmndrs/valtio/blob/main/docs/how-tos/how-to-use-with-context.mdx)
- [How to split and compose states](https://github.com/pmndrs/valtio/blob/main/docs/how-tos/how-to-split-and-compose-states.mdx)