Search code examples
reactjstypescriptreact-contextreact-typescriptreact-functional-component

How would I type correct the AuthCOntextProvider function so that on creation Typescript will not give me errors?


I'm currently learning Typescript and moving all the code of my project with Type Safety from Typescript but I still don't understand certain things like this one.

I am only trying to correctly type everything and trying to understand the process of how they work so.

This is my AuthContext.tsx file which exports the value of the currentUser easily for me throughout the React app

import React, { createContext, useEffect, useState } from 'react';
import { onAuthStateChanged, User } from 'firebase/auth';
import { auth } from '../firebase.config';

type Props = {
    children?: React.ReactNode;
};
type TAuthValue = {
    currentUser: User | null;
};

interface TAuthContext extends TAuthValue {
    (): JSX.Element;
}

export const AuthContextProvider = ({ children }: Props) => {
    const [currentUser, setCurrentUser] = useState<User | null>(null);

    useEffect(() => {
        const unsub = onAuthStateChanged(auth, (user) => {
            setCurrentUser(user);
        });

        return () => {
            unsub();
        };
    }, []);

    return <UserContext.Provider value={{ currentUser }}>{children}</UserContext.Provider>;
};

export const UserContext = createContext<TAuthContext>(AuthContextProvider);

The errors I receive when attempting to do it this way is:

ERROR in src/context/AuthContext.tsx:29:34

TS2322: Type '{ currentUser: User | null; }' is not assignable to type 'TAuthContext'.
  Type '{ currentUser: User | null; }' provides no match for the signature '(): Element'.
    27 |     }, []);
    28 |
  > 29 |     return <UserContext.Provider value={{ currentUser }}>{children}</UserContext.Provider>;
       |                                  ^^^^^
    30 | };
    31 |
    32 | export const UserContext = createContext<TAuthContext>(AuthContextProvider);


ERROR in src/context/AuthContext.tsx:32:56

TS2345: Argument of type '({ children }: Props) => JSX.Element' is not assignable to parameter of type 'TAuthContext'.
  Property 'currentUser' is missing in type '({ children }: Props) => JSX.Element' but required in type 'TAuthContext'.
    30 | };
    31 |
  > 32 | export const UserContext = createContext<TAuthContext>(AuthContextProvider);
       |                                                        ^^^^^^^^^^^^^^^^^^^
    33 |

I have tried reversing which type/interface extends which but they still provide errors. I looked at the documentation but I still am not sure what I'm doing wrong. Any guidance or pointers on where to look or an explanation would be greatly appreciated!


Solution

  • You're passing an entire component to createContext. But while using the context, you're passing it currentUser in the value prop. currentUser is of type User | null. Your context is of type (): JSX.Element which clearly have no overlap. Ideally, your UserContext should be of type User | null

    Check the docs on how to use Context

    export const UserContext = createContext<User | null>(null);