0

Im trying to achive sign-in functionality and pass user info to components tree using React Context:

// AuthProvider.tsx
import React, { createContext, useContext, useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import router from 'next/router';

type User = {
  name?: string
  error: boolean
}

const AuthContext = createContext<User>({} as User);

function AuthProvider({ children }: any): JSX.Element {
  const { pathname, events } = useRouter();
  const [user, setUser] = useState<User | null>({} as User);

  // called from effects
  async function getUser() {
    try {
      const response = await fetch('/api/auth/userinfo');
      const profile: User = await response.json();

      // if server send error field (no token or something...)
      if (profile.error) {
        setUser(null); // at this moment my backend always respond with 
// res.send({name: "Babushka", error: false}) - so no truthly error field here
      } else {
        setUser(profile as User);
        console.log('profile: ', profile); // i see {name: "Babushka"} 
// in Chrome console...
      }
    } catch (err) {
      console.error('Error: ', err)
    }
  }

  // call getUser on route change
  useEffect(() => {
    getUser()
  }, [pathname])


  // call from effect
  const handleChangeRoute = (url: string) => {
    if (url === '/restricted' && !user) {
      router.push('/login')
    }
  }

  useEffect(() => {
    // check if initial route is restricted
    if (pathname === '/restricted' && !user) {
      router.push('/login')
    }

    // subscribe for Nextjs router event
    events.on('routeChangeStart', handleChangeRoute)

    // unsubscripe
    return () => {
      events.off('routeChangeStart', handleChangeRoute)
    }
  }, [user])

  return (
    <AuthContext.Provider value={{user: user}} > // TS Error 
      { user && user?.name }   // i see Babushka here rendered too ...
      { children }
    </ AuthContext.Provider>
  )
}

const useAuth = () => useContext(AuthContext)

export { AuthProvider, useAuth }


For this demo purposes, I'm just want to test AuthContext for access user info in wrapped components, so i mock my api backend to send only this object {name: "Babushka", error: false} to make getUser result always be a non-empty object.

But next on my restricted page, where wrapped component with AuthProvider, I can't get user.

import { GetStaticProps } from 'next';
import { Group } from '../../types/Groups';
import {AuthProvider, useAuth} from '../../components/AuthProvider';

export default function RestrictedPage({ ...props }) {
  const {...user} = useAuth() // trying to destructure user info from Provider value, but getting always empty object {}
    return (
        <AuthProvider>            
            <MainPageWrapper> 
                <h1>Restricted area</h1>
                <p>{user && user.name}</p> // no render
            </MainPageWrapper>
        </AuthProvider>
    )
}

Do you see anything wrong with this code? Why i can get object passed from backend api, and it's dont affect on hooks ? And another question - TS Error in AuthContext says: *Type '{ user: User | null; }' is not assignable to type 'User'. Object literal may only specify known properties, and 'user' does not exist in type 'User'.*

This is obviously not a reason why hooks won't working, but i can't get why this error occurs. Glad to any help!

northernwind
  • 512
  • 5
  • 13
  • `const {...user} = useAuth()` this is the same as doing `const user = useAuth()`. You're doing both a destructure and a gather which cancel each other out. I think you mean to just do `const {user} = useAuth()`. – Brian Thompson Nov 16 '20 at 20:44
  • 1
    The problem is caused because you're trying to use the context at a higher level than the provider. Your `AuthProvider` needs to be a parent of the component accessing the `AuthContext` otherwise it can never be set. – Brian Thompson Nov 16 '20 at 20:48
  • @BrianThompson yes, thats was my mistake. I needed to wrap the consumer component into a separate function. Thank you! If you'd like to post this as answer, I will accept it. – northernwind Nov 16 '20 at 21:31
  • It's really just the same as these on [react-router](https://stackoverflow.com/questions/58220995/cannot-read-property-history-of-undefined-usehistory-hook-of-react-router-5/58221867#58221867) and [react-redux](https://stackoverflow.com/questions/60329421/usedispatch-error-could-not-find-react-redux-context-value-please-ensure-the/60329482#60329482) except with a custom hook. I don't think another answer would serve the public any better. – Brian Thompson Nov 16 '20 at 21:55

0 Answers0