How to Migrate to v2 from v1
Changes in v2
React 19 officially introduces the use
hook to handle promises.
Valtio v1 internally handled promises, which is no longer recommended.
In Valtio v2, promises are not handled internally,
and developers should explicitly use the use
hook to manage promises.
Note: If you are still using React 18, you can use the use
hook shim.
Valtio v2 also introduces two subtle changes in its design choices:
First, the behavior of proxy(obj)
has changed. In v1, it was a pure function and deeply copied obj
. In v2, it is an impure function and deeply modifies obj
. Generally, reusing obj
is not recommended. Unless you reuse obj
, nothing will break.
Second, the behavior of useSnapshot()
has been altered. Although it is a subtle change, it is less optimized to ensure compatibility with useMemo
and the upcoming React compiler. The change may lead to extra re-renders in some edge cases, but it might not be noticeable.
Other notable changes to keep things updated and fresh include:
- Removal of all deprecated features
- Requirement of React version 18 and above
- Requirement of TypeScript version 4.5 and above
- The build target updated to ES2018
Migration for breaking changes
Resolving promises
// v1
import { proxy, useSnapshot } from 'valtio'
const state = proxy({ data: fetch(...).then((res) => res.json()) })
const Component = () => {
const snap = useSnapshot(state)
return <>{JSON.stringify(snap.data)}</>
}
// v2
import { use } from 'react'
import { proxy, useSnapshot } from 'valtio'
const state = proxy({ data: fetch(...).then((res) => res.json()) })
const Component = () => {
const snap = useSnapshot(state)
return <>{JSON.stringify(use(snap.data))}</>
// If `data` is not an object, you can directly embed it in JSX.
// return <>{snap.data}</>
}
Impure proxy(obj)
If you don't reuse the object you pass to the proxy, nothing will break.
import { proxy } from 'valtio'
// This works in both v1 and v2
const state = proxy({ count: 1, obj: { text: 'hi' } })
// This works in both v1 and v2
state.obj = { text: 'hello' }
That's the recommended way to use proxy
.
For some reason, if you reuse the object, you need to use deepClone
explicitly in v2 to keep the same behavior as v1.
// v1
import { proxy } from 'valtio'
const initialObj = { count: 1, obj: { text: 'hi' } }
const state = proxy(initialObj)
// and do something later with `initialObj`
const newObj = { text: 'hello' }
state.obj = newObj
// and do something later with `newObj`
// v2
import { proxy } from 'valtio'
import { deepClone } from 'valtio/utils'
const initialObj = { count: 1, obj: { text: 'hi' } }
const state = proxy(deepClone(initialObj))
// and do something later with `initialObj`
const newObj = { text: 'hello' }
state.obj = deepClone(newObj)
// and do something later with `newObj`
useLayoutEffect
server warning
If you're using React 18 with SSR, add this conditional to prevent excessive warnings.
import { snapshot, useSnapshot as useSnapshotOrig } from 'valtio'
const isSSR = typeof window === 'undefined'
export const useSnapshot = isSSR ? (p) => snapshot(p) : useSnapshotOrig
// render with `useSnapshot` as usual