This is a living document that will be used to store my understanding of managing state on the Frontend.
React is a component based frontend framework.
The core piece of React is (state, props) => view.
Most state management solutions in React follow the flux architecture where a centralized store is updated through actions.
This also resembles what is known as the Elm Architecture.
This can be summarized as:
type Model = {};
type Action = { type: string, payload: { ... } };
type Reducer = <T extends unknown>(state: Model, action: Action) => {
switch action.type {
case 'update': {
Model.property = action.payload;
break;
}
...
}
}
// When state changes, render the UI again.
subscribe(state, () => <UI />);
This fundamental structure is the same in every single state management solution used in React today, some just make it more explicit than others.
import { proxy } from 'valtio';
const state = proxy({
message: 'hello';
})
function updateMessage(message: string) {
state.message = message;
}
function clearMessage() {
state.message = "";
}
// or
type MessageActions = {
type: 'update-message';
payload: string;
} | {
type: 'clear-message';
}
function reducer(action: MessageActions) {
switch (action) {
case 'update-message': {
state.message = action.payload;
break;
}
case 'clear-message': {
state.message = '';
break;
}
}
}
// or
const QueryCache = new QueryCache({});
let message = '';
let queryKey = ['message-query'];
const useMessageQuery = () => {
return useQuery(queryKey, () => {
return message;
});
}
const useUpdateQuery = () => {
return useMutation(async (message: string) => {
// Set the query cache directly
QueryCache.setQueryData(message);
// or set the state and invalidate the cache to cause a refetch
message = message;
QueryCache.invalidateQueries(queryKey);
});
};
// or in RxJS
const state = new BehaviorSubject();
const updateMessage = (msg: string) => state.next(msg);
At the end of the day all state management boils down to the same thing. Redux and other action based approaches resemble message passing in small-talk style object oriented programming, or rxjs pubsub, or just a traditional event-bus.
Traditional Redux encourages a single state store and reducer, you then define "slices" to figure out which path from the state you want to get, and those slices need to be memoized so that your components don't render when unrelated pieces of state change.
Atom based stores like Jotai, Zustand, etc.. encourage you to have multiple state stores, each one holding a smaller unit of state, the idea being that you can subscribe to smaller pieces of state and prevent unnecessary re-renders.
Stores like MobX and later Valtio keep track of the actual properties you are using from state to only render when something you are subscribed to changes. This can give you better performance and might make state easier to reason about for certain teams of developers.
Some people think that React Context is a valid mechanism for storing state, and while you can do this, it is really better suited as a dependency injection mechanism since React causes any component subscribed to the context to render both itself and the subtree.
Svelte's Reactivity
From the concept of looking at state management, svelte is the exact same model. It just has much nicer ergonomics for declaring and subscribing to state. Reactivity is a first class citizen in Svelte.
You have stores
const store = writable(... syntax I don't remember right now);
export function updateMessage(msg: string) {
message = msg;
}
// in your component
import { updateMessage } from './state';
// Will react to the message change
$: message = $store;
updateMessage('hello');
And so any guidance on managing atoms applies to svelte stores.
Nothing stops you from using Redux or another state management library with Svelte but the primitives it has are good enough to build a lot with them.
Vue
To be continued but it's essentially the same thing.
Talk about vuex and other state managers.
Top comments (0)