Home > Software engineering >  TypeScript on useContext
TypeScript on useContext

Time:10-24

I'm following a React tutorial on youtube where I'm trying to convert the Project from JavaScript to TypeScript, and I'm having a lot of trouble with the useContext, I would appreciate it if someone were to help me here. If you're wondering which tutorial here it is Tutorial

    import React, {createContext, useContext, useState } from 'react';
    
    const StateContext = createContext();
    
    export const ContextProvider = ({ children }) => {
        const [activeMenu, setActiveMenu] = useState(true);
    
        return (
            <StateContext.Provider value={{activeMenu, setActiveMenu}}>
                {children}
            </StateContext.Provider>
        )
    }
    
    export const useStateContext = () => useContext(StateContext)

CodePudding user response:

change this:

import React, {createContext, useContext, useState } from 'react';


const StateContext = createContext();

export const ContextProvider = ({ children }) => {
    const [activeMenu, setActiveMenu] = useState(true);

    return (
        <StateContext.Provider value={{activeMenu, setActiveMenu}}>
            {children}
        </StateContext.Provider>
    )
}

export const useStateContext = () => useContext(StateContext)

to this:

import React, {createContext, useContext, 
useState } from 'react';

interface Props {
children : ReactNode
}

const StateContext = createContext();

export const ContextProvider = ({ children }:Props) => {
    const [activeMenu, setActiveMenu] = useState(true);

    return (
        <StateContext.Provider value={{activeMenu, setActiveMenu}}>
            {children}
        </StateContext.Provider>
    )
}

export const useStateContext = () => useContext(StateContext)

CodePudding user response:

There are 3 options I've seen in the wild.

Option 1

This option is a bit annoying because the type of StateContext value can be null | StateContextType. But in the code you provided, it will only be null on creation and not null inside the provider. Regardless, everywhere you use useContext you'll have to have a guard against null.

import React, { createContext, useState } from 'react';

type StateContextType = {
  activeMenu: boolean;
  setActiveMenu: React.Dispatch<React.SetStateAction<boolean>>;
};

export const StateContext = createContext<null | StateContextType>(null);

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

export const ContextProvider = ({ children }: ContextProviderProps) => {
  const [activeMenu, setActiveMenu] = useState(true);
  const value = {
    activeMenu,
    setActiveMenu,
  };

  return (
    <StateContext.Provider value={value}>{children}</StateContext.Provider>
  );
};

Option 2

With this option, you cast StateContext value type to StateContextType. It means that the value of StateContext is assumed to be StateContextType. The only downside is that the value of StateContext is null for a very small amount of time at creation, before a value is provided in StateContext.Provider.

However, it's relatively safe since you're immediately passing a value in the provider.

import React, { createContext, useState } from 'react';

type StateContextType = {
  activeMenu: boolean;
  setActiveMenu: React.Dispatch<React.SetStateAction<boolean>>;
};

export const StateContext = createContext<StateContextType>(
  null as unknown as StateContextType,
);

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

export const ContextProvider = ({ children }: ContextProviderProps) => {
  const [activeMenu, setActiveMenu] = useState(true);
  const value = {
    activeMenu,
    setActiveMenu,
  };

  return (
    <StateContext.Provider value={value}>{children}</StateContext.Provider>
  );
};

Option 3

Credit to Steve Kinney's course on Frontend Masters on React and TypeScript for this one.

import React, { useState } from 'react';

export function createContext<T>() {
  const context = React.createContext<T | undefined>(undefined);

  const useContext = () => {
    const value = React.useContext(context);
    if (value === undefined) {
      throw new Error(
        `useContext must be used inside a Provider with a value that's not undefined`,
      );
    }
    return value;
  };
  return [useContext, context.Provider] as const;
}

type StateContextType = {
  activeMenu: boolean;
  setActiveMenu: React.Dispatch<React.SetStateAction<boolean>>;
};

export const [useContext, Provider] = createContext<StateContextType>();

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

export const ContextProvider = ({ children }: ContextProviderProps) => {
  const [activeMenu, setActiveMenu] = useState(true);
  const value = {
    activeMenu,
    setActiveMenu,
  };

  return <Provider value={value}>{children}</Provider>;
};

const Component = () => {
  // usage inside component
  const context = useContext();
  return <div></div>;
};

export const App = () => {
  return (
    <ContextProvider>
      <Component />
    </ContextProvider>
  );
};
  • Related