0

My code doesn't work, although it was made using this tutorial (link to exact file with AuthContext). This snippet in particular is a part of Auth Context. The only difference is that I code in typescript instead of JS. Tell me whether you need anything else to help me solve my issue.

useEffect(() => {
        const user = supabase.auth.getUser().user;
        setCurrentUser(user)

        const auth = supabase.auth.onAuthStateChange((event, session) => {
            if (event === 'SIGNED_IN') {
                setCurrentUser(session.user)
            }

            if (event === 'SIGNED_OUT') {
                setCurrentUser(null)
            }
        })

        return () => auth.data.unsubscribe()

    }, [])

Errors:

ERROR in src/contexts/AuthContext.tsx:76:46
TS2339: Property 'user' does not exist on type 'Promise<UserResponse>'.
    74 |
    75 |     useEffect(() => {
  > 76 |         const user = supabase.auth.getUser().user;
       |                                              ^^^^
    77 |         setCurrentUser(user)
    78 |
    79 |         const auth = supabase.auth.onAuthStateChange((event, session) => {

ERROR in src/contexts/AuthContext.tsx:81:32
TS2531: Object is possibly 'null'.
    79 |         const auth = supabase.auth.onAuthStateChange((event, session) => {
    80 |             if (event === 'SIGNED_IN') {
  > 81 |                 setCurrentUser(session.user)
       |                                ^^^^^^^
    82 |             }
    83 |
    84 |             if (event === 'SIGNED_OUT') {

ERROR in src/contexts/AuthContext.tsx:81:32
TS2345: Argument of type 'User' is not assignable to parameter of type 'SetStateAction<undefined>'.
  Type 'User' provides no match for the signature '(prevState: undefined): undefined'.
    79 |         const auth = supabase.auth.onAuthStateChange((event, session) => {
    80 |             if (event === 'SIGNED_IN') {
  > 81 |                 setCurrentUser(session.user)
       |                                ^^^^^^^^^^^^
    82 |             }
    83 |
    84 |             if (event === 'SIGNED_OUT') {

ERROR in src/contexts/AuthContext.tsx:85:32
TS2345: Argument of type 'null' is not assignable to parameter of type 'SetStateAction<undefined>'.
    83 |
    84 |             if (event === 'SIGNED_OUT') {
  > 85 |                 setCurrentUser(null)
       |                                ^^^^
    86 |             }
    87 |         })
    88 |

ERROR in src/contexts/AuthContext.tsx:89:32
TS2339: Property 'unsubscribe' does not exist on type '{ subscription: Subscription; }'.
    87 |         })
    88 |
  > 89 |         return () => auth.data.unsubscribe()
       |                                ^^^^^^^^^^^
    90 |
    91 |     }, [])
    92 |

Thanks in advance for help

Ken White
  • 123,280
  • 14
  • 225
  • 444
IVOBOT
  • 61
  • 8
  • *My code doesn't work* and *issues* are meaningless problem statements. Please [edit] your post to provide information describing the problem you're having with the code you've posted, and to ask a **specific question** related to that code. – Ken White Sep 29 '22 at 02:54

1 Answers1

1

Supabase v@2.0.0+

The available auth functions and their return types have changed slightly. They no longer offers the supabase.auth.user() function and only offer the promisable supabase.auth.getUser(). The return type of getUser is

export type UserResponse =
  | {
      data: {
        user: User
      }
      error: null
    }
  | {
      data: {
        user: null
      }
      error: AuthError
    }

I believe object deconstructing is also more readable so

const { data: { user } = await supabase.auth.getUser()

would be preferred.

This also address the return problem of onAuthStateChange(), return type of

{
  data: { subscription: Subscription }
}

So your code would change to

const { data: { subscription }} = supabase.auth.onAuthStateChange(callback);

Supabase v@1.0.0+ Supbase offers two functions retrieving the user, .user(). and .getUser(). .user() fetches the user from localStorage, which means it is a non-Promise function. .getUser() fetches the user from the server using a jwt, and thus is a Promise. In the tutorial they use .user() function, which why there is no await. In your code you use the .getUser() function. To continue to user the .getUser() function in the useEffect, you would have to call it inside async function

useEffect(() => {
  const fetchUser = async() => {
    const { user } = await supabase.auth.getUser();
    setCurrentUser(user);
  }
  fetchUser();
},[])

useState types To address the useState errors, you have to pass a type to your useState() function when the type of the state will be of a different type of the defaultValue you pass to it. const [str, setStr] = useState(''); infers the variable to be a string, while const [idk, setIdk] = useState(null) only infers the variable to be undefined, that's why it's throwing errors when you try to set currentUser to a variable of type User or null.

The way to fix this would be to pass the User type to useState

import { User } from '@supabase/supabase-js';

const [currentUser, setCurrentUser] = useState<User | null>(null);

Nullish checks To address the errors of 'Object could be null', all you have to do is add a nullish check, either in the form of an if/else statement

if(user) or if(user !== null) {
  setCurrentUser(user)
}

or adding a nullish variable check, `

const user = session?.user;

There is a slight difference between !user or user !== null, with the latter being less inclusive. See here

The docs for both supabase and typescript are great resources

If you don't have a particular need to create your own auth system for supabase, I would recommend supabase-auth-helpers.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Mason Clark
  • 201
  • 2
  • 7