Fabian Bosler
Fabian Bosler

Reputation: 2510

ContextApi, useReducer, & Typescript - calculated value is not accessible on component

Apologies for the somewhat opaque title, but I am having difficulties being more precise here.

So I have a Context/Reducer Logic, where I initialise the context with some values. I then have a reducer Logic on a custom Provider and use useMemo to calculate values. When trying to access one on of those values (that isn't in the state/initialState) on a component typescript gets angry at me and tells me that said value does not exist on State. What is the best way to remedy this warning?

I have the following definition of a Context/Reducer.

interface State {
  displaySidebar: boolean
}

const initialState = {
  displaySidebar: false
}

type Action =
  | {
      type: 'OPEN_SIDEBAR'
    }
  | {
      type: 'CLOSE_SIDEBAR'
    }

const UIContext = React.createContext<State>(initialState)
UIContext.displayName = 'UIContext'

const uiReducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'OPEN_SIDEBAR': {
      return {
        ...state,
        displaySidebar: true,
      }
    }
    case 'CLOSE_SIDEBAR': {
      return {
        ...state,
        displaySidebar: false,
      }
    }
  }
}

const UIProvider: FC = (props) => {
  const [state, dispatch] = React.useReducer(uiReducer, initialState)
  const openSidebar = (): void => dispatch({ type: 'OPEN_SIDEBAR' })
  const closeSidebar = (): void => dispatch({ type: 'CLOSE_SIDEBAR' })
  const value = useMemo(
    () => ({
      ...state,
      openSidebar,
      closeSidebar,
    }),
    [state]
  )
  return <UIContext.Provider value={value} {...props} />
}

export const useUI = () => {
  const context = React.useContext(UIContext)
  if (context === undefined) {
    throw new Error(`useUI must be used within a UIProvider`)
  }
  return context
}

export const ManagedUIContext: FC = ({ children }) => (
  <UIProvider>
    <ThemeProvider>{children}</ThemeProvider>
  </UIProvider>
)

now when I try to use const {closeSidebar} = useUI() in a component typescript gets angry with me and tells me that Property 'closeSidebar' does not exist on type 'State'. I get that, but I was not able to figure out how to properly add closeSidebar to the React.Context type.

Upvotes: 1

Views: 169

Answers (1)

Nadia Chibrikova
Nadia Chibrikova

Reputation: 5036

When you create context you tell TS that its type will be State, so it doesn't expect anything else to be there. If you want to add additional fields you can create an intersection type, state + methods, either as a named type of just React.createContext<State & {openSidebar : ()=> void, closeSidebar: ()=> void}>. Note that as your initial state doesn't have methods you either need to make them optional or provide some sort of dummy versions.

Upvotes: 1

Related Questions