Skip to content

Instantly share code, notes, and snippets.

@JulienPradet
Created May 17, 2024 12:04
Show Gist options
  • Save JulienPradet/cf0d13f301f375323fe84df1725fdf04 to your computer and use it in GitHub Desktop.
Save JulienPradet/cf0d13f301f375323fe84df1725fdf04 to your computer and use it in GitHub Desktop.
createStateActions as a simpler alternative to useReducer
// Example of a component using createStateActions
function Component() {
const [opened, setState] = useState(false);
const actions = createStateActions(setState, {
set: (currentState, value: boolean) => value,
open: () => true,
close: () => false,
toggle: currentState => !currentState,
});
return (
<div>
<button type="button" onClick={actions.open}>
Open
</button>
<button type="button" onClick={actions.close}>
Close
</button>
<button type="button" onClick={actions.toggle}>
Toggle
</button>
{opened && <p>Content</p>}
</div>
);
}
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Dispatch, SetStateAction, useState } from 'react';
type Action<TState, TArgs extends any[]> = (
currentState: TState,
...args: TArgs
) => TState;
type ActionArgs<TAction extends Action<any, any[]>> = TAction extends Action<
any,
infer TArgs
>
? TArgs
: never;
type TActionsCallback<
TState,
TActions extends Record<string, Action<TState, any[]>>
> = {
[Key in keyof TActions]: (...args: ActionArgs<TActions[Key]>) => void;
};
/**
* Each action performs a reducer like operation but this avoids
* verbosity on manually manipulating classic dispatch/type/payload
* of a useReducer
* @param actions an object similar to createSlice.reducers in redux toolkit
*/
function createStateActions<
TState,
TActions extends Record<string, Action<TState, any[]>>
>(
setState: Dispatch<SetStateAction<TState>>,
actions: TActions
): TActionsCallback<TState, TActions> {
return Object.keys(actions).reduce(
<TActionKey extends keyof TActions>(
callbacks: TActionsCallback<TState, TActions>,
actionKey: TActionKey
) => ({
...callbacks,
[actionKey]: (...args: ActionArgs<TActions[TActionKey]>) => {
setState(currentState => actions[actionKey](currentState, ...args));
},
}),
{} as TActionsCallback<TState, TActions>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment