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!