Context Provider in TypeScript

Viewed 2783

I got the simplest context provider working on ES6 but I am unable to transform it for use on a TypeScript project. Every website I search for has a completely different take on implementing context api.

It is just a boolean variable and a function to set it, I keep running into problems with TypeScript.

Do I need to change the entire approach to make it work on TypeScript?

context.js

import React from 'react'
import Reducer from './reducer'

export const LoadContext = React.createContext()

export const LoadProvider = ({ children }) => {
  const [state, dispatch] = React.useReducer(Reducer, {
    load: true
  })

  const setLoad = (load) => dispatch({ type: 'set_load', load })

  return (
    <LoadContext.Provider value={{ load: state.load, setLoad }}>
      {children}
    </LoadContext.Provider>
  )
}

export const useLoadContext = () => React.useContext(LoadContext)

reducer.js

export default (state, action) => {
  switch (action.type) {
    case 'set_load':
      return {
        load: action.load
      }
    default:
      return state
  }
}
2 Answers

Generally you would want to type your context, eg.:

type LoadContextType = {
  load: any // Not sure what these are, type it appropriately
  setLoad: any
}

const context LoadContext = React.CreateContext<LoadContextType>(null)

So when using either the provider or the consumer of LoadContext the types are known.

After a lot of sweating, I got it working with this:

context.tsx

import React, { FC } from 'react'
import Reducer from './reducer'


type LoadContextType = {
  load: boolean 
  setLoad: (load: boolean) => void
}

const InitialState = {
  load: true
}

export const LoadContext = React.createContext<LoadContextType | null>(null)

export const LoadProvider: FC = ({ children }) => {
  const [state, dispatch] = React.useReducer(Reducer, InitialState)

  const setLoad = (load: boolean): void => dispatch({ type: 'set_load', load })

  return (
    <LoadContext.Provider value={{ load: state.load, setLoad }}>
      {children}
    </LoadContext.Provider>
  )
}

export const useLoadContext = (): LoadContextType => React.useContext(LoadContext) as LoadContextType

reducer.tsx

interface stateProps {
  load: boolean 
}

interface actionProps extends stateProps {
  type: string
}

export default (state: stateProps, action: actionProps): stateProps => {
  switch (action.type) {
    case 'set_load':
      return {
        load: action.load
      }
    default:
      return state
  }
}

index.tsx

import React from 'react'
import ReactDom from 'react-dom'
import '@styles/styles.scss'

import { LoadProvider } from '@src/context/LoadContext'
import { App } from '@components/app'
ReactDom.render(
<LoadProvider>
  <App />
</LoadProvider>
, document.getElementById('root'))

App.tsx

import React, { ReactElement } from 'react'
import { useLoadContext } from '@src/context/LoadContext'

export const App = (): ReactElement => {
  const { load } = useLoadContext()
  console.log(load);
  
  return <h1>Getting context provider to work on TypeScript</h1>
}
Related