Home > Blockchain >  Context Provider Type and get the value on other components
Context Provider Type and get the value on other components

Time:05-26

I am using useContext along with useReducer to manage the application state.

Here follows the snippets:

//ReducerProvider.tsx

type ContextProviderType = {     
    state: StateInterface,
    dispatch: React.Dispatch<ActionInterface>
}

type CreateContextType = ContextProviderType | string; // i am passing to the createContext either the ContextProviderType or a string (the string is just to initialize without errors since useReducer is not declared yet)

export const ReducerContext = React.createContext<CreateContextType>('');

interface ProviderProps { 
    children: ReactNode
}

export const ReducerProvider: React.FC<ProviderProps> = ({children}): JSX.Element => {

    const [state, dispatch] = useReducer(reducer, initialState);
    
    return <>
        <ReducerContext.Provider value={{state, dispatch}}>
            {children}
        </ReducerContext.Provider>
    </>
}

//App.tsx

const App: React.FC = (): JSX.Element => {

  return <>
        <ReducerProvider>
          //components here so they can take the value of the provider component
        </ReducerProvider>
    </>;

}

The error only comes up when I try to get the state and dispatch values, like this:

//Component.tsx

export const Component: React.FC = (): JSX.Element => {

  const { state, dispatch } = useContext(ReducerContext)
  //ERROR: Property 'state' does not exist on type 'CreateContextType'
  //ERROR: Property 'dispatch' does not exist on type 'CreateContextType'

  return (
    <div className="box">
        //...
    </div>
  )
}

But I'm not understanding this error since I declared that CreateContextType can be either a string or a ContextProviderType, which is an object that receives state and dispatch parameters.

CodePudding user response:

But I'm not understanding this error since I declared that CreateContextType can be either a string or a ContextProviderType,

That's precisely why you're getting the error: you said it might be a string. In the event that it's a string, then it won't have a state and dispatch property. Since it might not have those properties, typescript won't let you access them unless you write code to make sure they're there first.

Checking for a string might look like this:

let state;
let dispatch;
const value = useContext(ReducerContext);
if (typeof value === 'string') {
  throw new Error('Uh oh, i got a string');
} else {
  ({ state, dispatch } = value;
}

That's obviously pretty cumbersome, so you could extract this checking to a custom hook:

export const useReducerContext = () => {
  const value = useContext(ReducerContext);
  if (typeof value === 'string') {
    throw new Error("Don't forget to render a <ReducerProvider> higher up the tree")
  }
  return value;
}

// used like:
const { state, dispatch } = useReducerContext();

The other option is to change the type on ReducerContext, so that it's just using ContextProviderType, not string. This does mean you'll need to provide a full default value:

export const ReducerContext = React.createContext<ContextProviderType>({
  state: {
    // Fill out an entire fake state object here
  },
  dispatch: () => {}
});

Making a default value which will never be used can be a pain, so you could use a type assertion to shut typescript up:

export const ReducerContext = React.createContext<ContextProviderType>(
  {} as ContextProviderType
)
  • Related