Search code examples
reactjstypescriptreact-context

React Context & Typescript with Custom Hook


I have created the following context provider using React & Chakra-UI:

import { useBoolean } from "@chakra-ui/react"
import { createContext } from "react"

const MobileContext = createContext<typeof useBoolean | undefined>(undefined)

function MobileProvider({ children }) {
  const [show, setShow] = useBoolean(false)

  return (
    <MobileContext.Provider value={{ show, setShow }}>
      {children}
    </MobileContext.Provider>
  )
}

Unfortunately, the above code does not seem to work. I get a red squiggly line under show in value={{ show, setShow }} with the following message:

Type '{ 
  show: boolean; 
  setShow: { 
    readonly on: () => void; 
    readonly off: () => void;  
    readonly toggle: () => void; 
  }; 
}' is not assignable to type 
  '(initialState?: InitialState) => 
  readonly [
      boolean, 
      { 
        readonly on: () => void; 
        readonly off: () => void; 
        readonly toggle: () => void; 
      }
   ]'.
  

Object literal may only specify known properties, and 'show' does not exist in type 

  '(initialState?: InitialState) => 
    readonly [
      boolean, 
      { 
        readonly on: () => void; 
        readonly off: () => void; 
        readonly toggle: () => void; 
      }
   ]'
   .ts(2322)
   index.d.ts(336, 9): 

The expected type comes from property 'value' which is declared here on type 
  'IntrinsicAttributes & ProviderProps<(initialState?: InitialState) => 
    readonly [
      boolean, 
      { 
         readonly on: () => void; 
         readonly off: () => void; 
         readonly toggle: () => void; 
      }
    ]
  >'

I think I understand what the problem is -- that the return value of useBoolean is not exactly the same as show and setShow (although perhaps I am wrong). Either way, I can't figure out how to get this to work properly.

Any ideas?

Thanks.


Solution

  • Since you are providing the return value of useBoolean, and you shape the context value differently, you should replace

    const MobileContext = createContext<typeof useBoolean | undefined>(undefined)
    
    

    With

    type UseBooleanReturn = ReturnType<typeof useBoolean>
    
    const MobileContext = createContext<{show: UseBooleanReturn[0], setShow: UseBooleanReturn[1] } | undefined>(undefined)