Skip to content
Daishi Kato edited this page Jul 18, 2021 · 11 revisions

Ref: https://github.com/pmndrs/valtio/issues/171

This is to describe the high level abstraction of valtio.

proxy() by examples

import { proxy, subscribe } from 'valtio'

const s1 = proxy({})
subscribe(s1, () => { console.log('s1 is changed!') })
s1.a = 1 // s1 is changed!
++s1.a // s1 is changed!
delete s1.a // s1 is changed!
s1.b = 2 // s1 is changed!
s1.b = 2 // (not changed)
s1.obj = {} // s1 is changed!
s1.obj.c = 3 // s1 is changed!
const s2 = s1.obj
subscribe(s2 () => { console.log('s2 is changed!') })
s1.obj.d = 4 // s1 is changed! and s2 is changed!
s2.d = 5 // s1 is changed! and s2 is changed!
const s3 = proxy({})
subscribe(s3 () => { console.log('s3 is changed!') })
s1.o = s3
s3.p = 'hello' // s1 is changed! and s3 is changed!
s2.q = s3
s3.p = 'hi' // s1 is changed! s2 is changed! and s3 is changed!
s1.x = s1
s1.a += 1 // s1 is changed!

snapshot() by examples

useSnapshot() by examples

Unorganized Notes

two kinds of proxies

valtio has two kinds of proxies, for write and read. We intentionally separate them for hooks and concurrent react.

proxy() creates a proxy object to detect mutation, "proxy for write" snapshot() creates an immutable object from the proxy object useSnapshot() wraps the snapshot object again with another proxy (with proxy-compare) to detect property access, "proxy for read"

snapshot creation is optimized

const state = proxy({ a: { aa: 1 }, b: { bb: 2 } })
const snap1 = snapshot(state)
console.log(snap1) // ---> { a: { aa: 1 }, b: { bb: 2 } }
++state.a.aa
const snap2 = snapshot(state)
console.log(snap2) // ---> { a: { aa: 2 }, b: { bb: 2 } }
snap1.b === snap2.b // this is `true`, it doesn't create a new snapshot because no properties are changed.

Some notes about valtio implementation in deep

valtio's proxy has only one goal: create an immutable snapshot object

some design principles:

  1. snapshot is created on demand
  2. changes are tracked only with version number
  3. subscription is used for notifying update (version)
  4. version number is hidden as implementation detail
  5. proxies are basically used only for version and subscription
  6. snapshot creation is optimized with version number

some notes about the implementation:

  1. proxy can be nested (created at the initialization)
  2. proxy can have circular structure (globalVersion to detect it)

some notes about promise handling:

  1. proxy can have a promise but does nothing
  2. when creating a snapshot, it will store the resolved value
  3. if it's not resolved, a special object will throw a promise/error

Suggestion notes:

  1. Starting with useSnapshot() what proxies are created and what are their conceptual names? Briefly, how is useMutableSource() source used to setup the subscription? It looks like there are listeners on a proxy, for deep property changes, how are those stored and tracked? What will trigger the subscription to be fired? Be detailed here.
  2. Walk through the flow of a property change for something non-trivial like address.person.name = "Bob" Step-by-step, from the moment of the assignment, what happens? How is the change notification queued? Is it through a React hook? When the change notification actually occurs, how is a re-render triggered? Leave no stone unturned as this is a very complicated interaction.