Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using react context with react hooks in typescript

Code below demonstrates how I'm trying to implement react's context with react hooks, idea here is that I will be able to easily access context from any child component like this

const {authState, authActions} = useContext(AuthCtx);

To begin with I create a file that exports context and provider.

import * as React from 'react';

const { createContext, useState } = React;

const initialState = {
  email: '',
  password: ''
};

const AuthCtx = createContext(initialState);

export function AuthProvider({ children }) {
  function setEmail(email: string) {
    setState({...state, email});
  }

  function setPassword(password: string) {
    setState({...state, password}); 
  }

  const [state, setState] = useState(initialState);
  const actions = {
    setEmail,
    setPassword
  };

  return (
    <AuthCtx.Provider value={{ authState: state, authActions: actions }}>
      {children}
    </AuthCtx.Provider>
  );
}

export default AuthCtx;

This works, but I get error below in value of provider, probably because I add actions in, hence the question, is there a way for me to keep everything typed and still be able to export context and provider?

I beliebe I also can't place createContext into my main function since it will re-create it all the time?

[ts] Type '{ authState: { email: string; password: string; }; authActions: { setEmail: (email: string) => void; setPassword: (password: string) => void; }; }' is not assignable to type '{ email: string; password: string; }'. Object literal may only specify known properties, and 'authState' does not exist in type '{ email: string; password: string; }'. [2322] index.d.ts(266, 9): The expected type comes from property 'value' which is declared here on type 'IntrinsicAttributes & ProviderProps<{ email: string; password: string; }>' (property) authState: { email: string; password: string; }

like image 712
Ilja Avatar asked Nov 16 '18 10:11

Ilja


People also ask

Can you use React Hooks with TypeScript?

Evidently, using TypeScript with React hooks is easier than using it with React classes. And because strong typing is a valuable security for code safety, you should consider using TypeScript if your new project uses hooks. You should definitely use hooks if you want some TypeScript.

Is useContext a React hook?

The useContext hook is the new addition in React 16.8. Syntax: const authContext = useContext(initialValue); The useContext accepts the value provided by React.

When should I use useContext React?

When do you need context? The main idea of using the context is to allow your components to access some global data and re-render when that global data is changed. Context solves the props drilling problem: when you have to pass down props from parents to children.


2 Answers

Answer above works, because strict rules of checking of types disabled. Example for context with strict rules:

import { createContext, Dispatch, SetStateAction, useState } from 'react';
import { Theme } from '@styles/enums';
import { Language } from '@common/enums';

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

type Context = {
  appLang: string;
  appTheme: string;
  setContext: Dispatch<SetStateAction<Context>>;
};

const initialContext: Context = {
  appLang: Language.EN,
  appTheme: Theme.DEFAULT,
  setContext: (): void => {
    throw new Error('setContext function must be overridden');
  },
};

const AppContext = createContext<Context>(initialContext);

const AppContextProvider = ({ children }: Props): JSX.Element => {
  const [contextState, setContext] = useState<Context>(initialContext);

  return (
    <AppContext.Provider value={{ ...contextState, setContext }}>
      {children}
    </AppContext.Provider>
  );
};

export { AppContext, AppContextProvider };

It works for me. Theme and Language it just enums, like this:

export enum Theme {
  DEFAULT = 'DEFAULT',
  BLACK = 'BLACK',
}

And I send Error function in setContext inner initialContext for throwing error if programmer doesn't define setContext in Provider. You can just use

setContext: (): void => {}

Good luck!

like image 143
Роман Татаринов Avatar answered Oct 14 '22 05:10

Роман Татаринов


While creating Context, you are providing an initial value to it. Provide it in the same format as you expect it to be for the Provider like:

const initialState = {
  authState : { 
      email: '',
      password: ''
  },
  authActions = {
    setEmail: () => {},
    setPassword: () => {}
  };
};

const AuthCtx = createContext(initialState);

Also, you don't even need the initialState since its only passed to Consumer, if you don't have a Provider higher up in the hierarchy for the Consumer.

like image 25
Shubham Khatri Avatar answered Oct 14 '22 03:10

Shubham Khatri