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; }
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.
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 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.
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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With