Search code examples
reactjstypescriptreact-tsx

useReducer type with optional dispatch


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.


Solution

  • Using 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

    Using 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!