Using Valtio for Data Storage in React

Atomic State Management

RIAZ VIRANI

Website: riazv.me

About me

- Live in Toronto

- Senior Director, Software Engineering

- Work for a WasteDynamics, a waste logistics company based out of Cleveland

 

- Love labelmakers and spreadsheets

- Will read anything by Mark Manson

Developer community I help run. Come join us!

What are we Going to TAlk ABout

React is View Only

React doesn't provide an opinionated way to store complex information from your API or internal to your application in the browser

  • Doesn't batch updates
  • Coupled to specific component
  • Leads to prop drilling

useState()

function HigherComponent () {
  const [data, setData]= useState({
    word: '', foo: false
  })

  return (
    <SomeComponent
      data={data}
      setData={setData}
    />
  )
}

function LowerComponent ({ data, setData }) {
  return (
    <div>
      <p>{data.word}</p>
      <button onClick={() => {
         setData({...data, word: 'Yo'})
      }}>
        Click Me
      </button>
    </div>
  )
}
  • Excessive re-renders
  • Component Coupling

useContext()

const ThemeContext = createContext();

function ThemeProvider ({ children }) {
  const [theme, setTheme] = useState('light');
  const toggleTheme = () => setTheme(
    theme === 'light' ? 'dark' : 'light'
  );

  return (
    <ThemeContext.Provider
      value={{ theme, toggleTheme }}
    >
      {children}
    </ThemeContext.Provider>;
  )
}

function ThemeSwitcher() {
  const { theme, toggleTheme } = useContext(
    ThemeContext
  );

  return (
    <div>
      <p>Current Theme: {theme}</p>
      <button onClick={toggleTheme}>
        Toggle Theme
      </button>
    </div>
  );
}
Recoil Redux Jotai
XState MobX Zustand
Akita Hookstate Apollo Client
Easy-peasy constate swr
tanstack-query effector rxdb
watermelondb reselect kea

Other Libraries

What is valtio

Valtio makes proxy state easy for your React application

What is valtio

Proxy Based

Uses native Javascript proxy to create observable state objects.

Immutable State

Allows immutable state updates. When state changes are made, new state objects are created rather than modifying the existing ones.

Minimal Boilerplate

Very little boilerplate to setup and update state.

Javascript Proxy

- Introduced in ES6

- Is an object that wraps another object or function and intercepts its fundamental operations, such as property access, assignment, invocation, and more.

const target = {
  message1: "hello",
  message2: "everyone",
};

const handler2 = {
 get(target, prop, receiver) {
    if (prop === "message2") {
      return "world";
    }
    return Reflect.get(...arguments);
  },
};

const proxy = new Proxy(target, handler2);

console.log(proxy.message1); // hello
console.log(proxy.message2); // world
const target = {
  message1: "hello",
  message2: "everyone",
};

const handler2 = {
 get(target, prop, receiver) {
    if (prop === "message2") {
      return "world";
    }
    return Reflect.get(...arguments);
  },
};

const proxy = new Proxy(target, handler2);

console.log(proxy.message1); // hello
console.log(proxy.message2); // world

Valtio can be used in vanilla Javascript.

 

This also means we can use Valtio states in older React Class components.

Why this Matters

import { proxy, snapshot } from 'valtio'

const store = proxy({ name: 'Mika' })
const snap1 = snapshot(store) // an efficient copy of the current store values, unproxied

const snap2 = snapshot(store)
console.log(snap1 === snap2) // true, no need to re-render


store.name = 'Hanna'
const snap3 = snapshot(store)
console.log(snap1 === snap3) // false, should re-render
  • You can make changes to it in the same way you would to a normal js-object.
  • Proxy tracks changes to the object and all nested proxy objects.
  • Multiple changes are batched and changes with the same value are not recorded.

React Implementation

import { proxy } from 'valtio'

const personState = proxy({ 
  name: 'Timo', 
  role: 'admin' 
})

const authState = proxy({ 
  status: 'loggedIn', 
  user: personState 
})

console.log(authState.user.name) // would return Timo

authState.user.name = 'Nina'
console.log(authState.user.name) // would return Nina
  • useSnapshot creates a local snapshot of the state that triggers a re-render of the component on the key change.
  • It wraps the Valtio snapshot in an access-tracking proxy to make sure the component or child component only re-renders on the specific key change.

Read State With snapshots

import { proxy, useSnapshot } from 'valtio'

const personState = proxy({ 
  name: 'Timo', 
  role: 'admin' 
})

const authState = proxy({ 
  status: 'loggedIn', 
  user: personState 
})


function UserProfile() {
  const snap = useSnapshot(
    authState.user
  )
  return (
    <div>
      {snap.name}
    </div>
  )
}

Look Out!

Don't re-assign the proxy to a whole new object.

Then state will stop working as expected.

let state = proxy({ 
  user: { 
    name: 'Timo' 
  } 
})

subscribe(state, () => {
  console.log(state.user.name)
})
// will not notify subscribers
state = { 
  user: { 
    name: 'Nina' 
  } 
}
// DON'T DO THIS

Instead

Mutate the object.

let state = proxy({ 
  user: { 
    name: 'Timo' 
  } 
})

subscribe(state, () => {
  console.log(state.user.name)
})

state.user.name = 'Nina'

1 more cool thing

It works with Redux Dev tool!!

import { devtools } from 'valtio/utils'

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

const unsub = devtools(state, { 
  name: 'state name', 
  enabled: true 
})

Read More

Questions?

Made with Slides.com