Redux (+ React)
Redux is a genre-defining state management library, most often paired with React. The library is built around three core concepts, each of which can benefit from Variant. They are:
- Actions
- Store
- Reducers
Action definitons created through variant() are more flexible than the createSlice() function because they can be used anywhere. They aren't tied to redux thunk middleware, and the instances they create can be serializable.
The store type definition can be greatly enhanced through discriminated unions. I love to use variants to express user config or loading states. I even use them as part of my store to represent the current view.
The match function was designed to be the ideal reducer. It enables expressive syntax that clearly captures the intent of the programmer.
Real-life exampleโ
I've pulled some code from an app I'm building to demonstrate how Variant fits in at each stage of the process.
Actionsโ
My actions are defined as a variant.
tsexport const SettingsAction = variant({SetColorMode: payload<ColorMode>(),SetUIGravity: payload<UIGravity>(),})export type SettingsAction<T extends TypeNames<typeof SettingsAction> = undefined> = VariantOf<typeof SettingsAction, T>;
tsexport const SettingsAction = variant({SetColorMode: payload<ColorMode>(),SetUIGravity: payload<UIGravity>(),})export type SettingsAction<T extends TypeNames<typeof SettingsAction> = undefined> = VariantOf<typeof SettingsAction, T>;
The ColorMode and Gravity types are defined as belowโ
tsexport const ColorMode = catalog(['light','dark','system',]);export type ColorMode = keyof typeof ColorMode;export type UIGravity = 'top' | 'bottom';
tsexport const ColorMode = catalog(['light','dark','system',]);export type ColorMode = keyof typeof ColorMode;export type UIGravity = 'top' | 'bottom';
Storeโ
I create an interface WebAppSettings comprised of my catalog type. This may store variants as wellย โ ColorMode could have been a variant with two options, system and selected (light/dark).
tsexport interface WebAppSettings {/*** App color mode (light or dark mode)*/colorMode: ColorMode;/*** Whether the UI chrome should be above or below the content.*/gravity: UIGravity;}export interface RootState {// ...settings: WebAppSettings;}
tsexport interface WebAppSettings {/*** App color mode (light or dark mode)*/colorMode: ColorMode;/*** Whether the UI chrome should be above or below the content.*/gravity: UIGravity;}export interface RootState {// ...settings: WebAppSettings;}
Reducerโ
The match() function steps in to assist with action processing.
tsexport const settingsReducer = (state = initState, action: SettingsAction) => {return produce(state, s => { // Immermatch(action, {SetColorMode({payload}) {s.settings.colorMode = payload;},SetUIGravity({payload}) {s.settings.gravity = payload;},})})}
tsexport const settingsReducer = (state = initState, action: SettingsAction) => {return produce(state, s => { // Immermatch(action, {SetColorMode({payload}) {s.settings.colorMode = payload;},SetUIGravity({payload}) {s.settings.gravity = payload;},})})}