Atomic State Management
RIAZ VIRANI
Website: riazv.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!
React doesn't provide an opinionated way to store complex information from your API or internal to your application in the browser
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>
)
}
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 |
Valtio makes proxy state easy for your React application
Uses native Javascript proxy to create observable state objects.
Allows immutable state updates. When state changes are made, new state objects are created rather than modifying the existing ones.
Very little boilerplate to setup and update state.
- 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.
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
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.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>
)
}
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
Mutate the object.
let state = proxy({
user: {
name: 'Timo'
}
})
subscribe(state, () => {
console.log(state.user.name)
})
state.user.name = 'Nina'
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
})