Home > Software engineering >  How to use typescript with createContext when passing state variables as the provider value?
How to use typescript with createContext when passing state variables as the provider value?

Time:01-06

I am trying to use typescript with createContext. I am making a context wrapper class NavContext and passing state variables as value to the provider.

Here is the code:

export const NavContext = createContext();

const NavBar:ReactFC<ReactNode | undefined>= ({ children }) => {
    const [isDrawerOpen, setIsDrawerOpen] = useState(false);

    return (
        <NavContext.Provider value={{ isDrawerOpen, setIsDrawerOpen }}>
            <AppBar position="static">{children}</AppBar>
        </NavContext.Provider>
    );
};

But I get caret symbol ^^^^^^ beneath createContext() and get typescript error as

TS2554: Expected 1 arguments, but got 0.

Please guide me on how to resolve this error as without typescript this code worked fine.

CodePudding user response:

This is the way I set up context with TS, by providing a default value:

Set up NavbarContext.tsx:

type NavContextType = {
  isDrawerOpen: boolean;
  setIsDrawerOpen: React.Dispatch<React.SetStateAction<boolean>>;
}

export const NavContext = createContext<NavContextType | null>(null);

type ProviderType = {
  children: React.ReactNode;
};

export default function NavBarStateProvider({ children }: ProviderType) {
  const [isDrawerOpen, setIsDrawerOpen] = useState(false);

  return (
    <NavContext.Provider value={{ isDrawerOpen, setIsDrawerOpen }}>
      {children}
    </NavContext.Provider>
  );
}

Custom hook to use the context, useNavContext.tsx:

export default function useNavContext() {
  const context = useContext(NavContext);
  if (!context) {
    throw new Error("useNavContext must be used within a NavBarStateProvider");
  }
  return context;
}

CodePudding user response:

The problem is that contexts can be consumed without necessarily being a child of a Provider. Even if your app doesn't do this anywhere, you need to make TypeScript happy by passing the default context value to .createContext. (Because the context value type looks to have a state setter in it, I'd assume that there will always be a provider - because you can't have a state setter outside the context of a React component.)

One option is to pass a meaningless argument to createContext (just because it's required) and tell TypeScript that it's OK that the type doesn't match. Also note that createContext should have a generic argument as well.

type NavContextType = {
  isDrawerOpen: boolean;
  setIsDrawerOpen: React.Dispatch<React.SetStateAction<boolean>>;
};
export const NavContext = createContext<NavContextType>(undefined!);

CodePudding user response:

You should define the types for provider and context clearly.

interface ProviderProps {
  children: React.ReactNode
}

interface ContextValue {
  isDrawerOpen: boolean;
  setIsDrawerOpen: () => void;     
}

export const NavContext = createContext<ContextValue>(
  null as unknown as ContextValue
)

const NavBar = ({ children }: ProviderProps) => {
    const [isDrawerOpen, setIsDrawerOpen] = useState(false);

    return (
        <NavContext.Provider value={{ isDrawerOpen, setIsDrawerOpen }}>
            <AppBar position="static">{children}</AppBar>
        </NavContext.Provider>
    );
};
  • Related