typescriptuse-stateprovideruse-contextcreatecontext

useState with useContext in Typescript


Lets say I have a basic setup for checking whether a user is logged in or not:

import { createContext } from "react";
const UserContext = createContext<string | null>(null)
export default UserContext

In my App.tsx I want to create a useState hook so that I can manage the state of my context throughout my application:

//Context
const [context, setContext] = useState<string | null>(null)

  <UserContext.Provider value={{context, setContext}}>

    <Routes>

      <Route path="/" element={<Home/>}/> 
      <Route path="/login" element={<Login/>}/> 
      <Route path="/register" element={<Register/>}/> 
      <Route path="/admin" element={<Admin/>}></Route>

    </Routes>  

  </UserContext.Provider>

So as far as I can see I'll only ever need either the name of the logged in user, or set the state of the context to null if I want to deny access to certain pages. Now the issue is that typescript is yelling at me here, specifically in the value of the Provider:

Type '{ context: string | null; setContext: 
React.Dispatch<React.SetStateAction<string | null>>; }' is not assignable to type 'string'.ts(2322)

I can force cast the value as follows:

value={{context, setContext} as any}

But that doesn't seem like a particularly elegant 'typescripty' solution.

Any help is appreciated!


Solution

  • You've typed your context as:

    string | null
    

    But then you provide a value for that context of (approxiamately) type:

    { context: string | null, setContext: (newString) => void }
    

    So if you want the state setter in your context, then it needs to be part of the context's type:

    const UserContext = createContext<{
      context: string | null,
      setContext: (newValue) => void
    }>({
      context: null,
      setContext: () => undefined
    })