Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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

Awesome Lists containing this project

README

        

valtio



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 18

const 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.



image

#### 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)