I need help with adding types to a reducer function with optional dispatch. Here's the source of the pattern. https://twitter.com/FernandoTheRojo/status/1521312171262681090?s=20&t=oerzPqJ8cb5Ts3sHVMH_5Q
Here's the code:
[on, toggle] = useReducer(
(prevState, next) => next == null ? !prevState : next,
false
)
And here is my attempt:
const [on, toggle] = useReducer(
(prevState: boolean, next?: boolean) => (next == null ? !prevState : next),
false
)
const enable = () => toggle(true) // Error Expected 0 arguments, but got 1.ts(2554)
const disable = () => toggle(false) // Error Expected 0 arguments, but got 1.ts
const toggleSwitch = () => toggle() // ☑️
And here's the codesandbox with a very similar example. https://codesandbox.io/s/usereducer-patterns-6gvrgh?file=/src/reducerHooks/ToggleOrSet.tsx
Thank you.
useReducer()
(not preferred)This is about as close as it gets to the example you posted with type safety:
import { Reducer, useReducer } from 'react';
export function Toggler() {
const [on, toggle] = useReducer<Reducer<boolean, boolean | undefined>>(
(prevState: boolean, action?: boolean) => (action === undefined ? !prevState : action),
false,
);
const enable = () => toggle(true);
const disable = () => toggle(false);
const toggleSwitch = () => toggle(undefined);
}
Again, I want to re-iterate that this is exactly why you should be using useState for this type of logic. useState is a basic Hook for managing simple state transformation, and useReducer is an additional Hook for managing more complex state logic
useState()
(preferred)import { useState } from 'react';
export function Toggler() {
const [on, setOn] = useState(false);
const enable = () => setOn(true);
const disable = () => setOn(false);
const toggle = () => setOn(currentOn => !currentOn);
}
The useState
implementation is cleaner, easier to read and even smaller!