Skip to content
Michel van den Berg edited this page Aug 31, 2021 · 13 revisions

useSnapshot(state) without property access will always trigger re-render

https://github.com/pmndrs/valtio/issues/209#issuecomment-896859395

Suppose we have this state (or store).

const state = proxy({
  obj: {
    count: 0,
    text: 'hello',
  },
})

If using the snapshot with accessing count,

const snap = useSnapshot(state)
snap.obj.count

it will re-render only if count changes.

If the property access is obj,

const snap = useSnapshot(state)
snap.obj

then, it will re-render if obj changes. This includes count changes and text changes.

Now, we can subscribe to the portion of the state.

const snapObj = useSnapshot(state.obj)
snapObj

This is technically same as the previous one. It doesn't touch the property of snapObj, so it will re-render if obj changes.

In summary, if a snapshot object (nested or not) is not accessed with any properties, it assumes the entire object is accessed, so any change inside the object will trigger re-render.

Object.keys

Object.keys(state.obj) doesn't touch any object properties, so the same as above.

It's possible to use non-proxy rendering:

const [keys, setKeys] = useState([]);
useEffect(() => {
  const callback = () => {
    const next = Object.keys(state.obj);
    setKeys((prev) => (prev.length === next.length && prev.every((k) => next.includes(k)) ? prev: next);
  }
  const unsubscribe = subscribe(state.obj, callback);
  callback(); // run just once
  return unsubscribe;
}, [])