valtio
@umijs/max comes with a built-in valtio data flow solution.
Enable valtio
Configure to enable.
export default {valtio: {},}
Getting Started
Basic Usage
Extremely simple.
import { proxy, useSnapshot } from 'umi';// 1. Define dataconst state = proxy({ count: 0 });// 2. Use dataconst snap = useSnapshot(state);snap.count;// 3. Update datastate.count += 1;
Access Outside React
Naturally supported.
import { proxy } from 'umi';const state = proxy({ count: 0 });state.count;state.count += 1;
Data Deduction
import { proxyWithComputed } from 'umi';const state = proxyWithComputed({count: 0,}, {double: snap => snap.count * 2,});
Actions and Async Actions
Two ways to use, can be combined with state or separated.
import { proxy } from 'umi';// Method one: Combineconst state = proxy({count: 0,actions: {add() {// Note, do not use this.count, will error when called based on snapstate.count += 1;},}});// Method two: Separateconst state = proxy({ count: 0 });const actions = {add() {state.count += 1;},// Async actionasync addAsync() {state.count += await fetch('/api/add');},};
Splitting and Combining Data Structures
import { proxy } from 'umi';// For example, as follows defined// state.foo and state.bar are both proxies, can be split and usedconst state = proxy({foo: { a: 1 },bar: { b: 1 },});// Combinationconst foo = proxy({ a: 1 });const bar = proxy({ b: 1 });const state = proxy({ foo, bar });
Component Encapsulation
If the content of props is unrelated to state, it can be left unhandled; if related, wrap it with context as follows, while synchronizing props to state data.
import { proxy } from 'umi';// 1. createContextconst MyContext = createContext();// 2. Providerconst value = useRef(proxy({ count: 0 })).current;<MyContext.Provider value={value} />// 3. useContextuseContext(MyContext);
Redux DevTools Support
import { proxy, proxyWithDevtools } from 'umi';const state = proxy({ count: 0 });proxyWithDevtools(state, { name: 'count', enabled: true });
Redo & Undo Support
import { proxyWithHistory } from 'umi';const state = proxyWithHistory({count: 0,});state.value.count;state.value.count += 1;state.undo();state.redo();state.history;
Persistent Cache
To be implemented.
import { proxyWithPersistent } from 'umi';const state = proxyWithPersistent({count: 0,}, {type: 'localStorage',key: 'count',});
Extension
Valtio is based on a composable extension approach, which provides better type hints compared to the middleware approach. For instance, to implement the earlier mentioned proxyWithPersistent, a simpler solution would be as follows,
export function proxyWithPersist<V>(val: V, opts: {key: string;}) {const local = localStorage.getItem(opts.key);const state = proxy(local ? JSON.parse(local) : val);subscribe(state, () => {localStorage.setItem(opts.key, JSON.stringify(snapshot(state)));});return state;}
Compatibility
- Requires React 16.8 or above, 2) Does not support IE 11, 3) map and set cannot be used directly, need to use the valtio provided proxyMap and proxySet instead.
import { proxy, proxyMap } from 'umi';const state = proxy({todos: proxyMap<number, Todo>([[1, {id:1,text:'Learn Umi'}]]),filter: 'all',});
Testing
You can test the store directly, or test React components based on the store. Write cases as usual, the latter are recommended to use @testing-library/react.