The Need:
I am using RedwoodJS for making a Fullstack app. It provides us with hook useAuth()
which gives a state isAuthenticated
telling if user is logged in or not.
I want to make some Queries on Web side whenever user Logs in. Not whenever isAuthenticated
from useAuth()
is changed. (example: on page loads, isAuthenticated
is set to false
from undefined
.. but this doesn't mean user logged out. What if I want to run certain function only on log out?
Tried Solution:
I wrote this custom hook:
export type CurrentUser = ReturnType<typeof useAuth>['currentUser']
interface HookProps {
// similarly for onLogout
onLogin?: (currentUser: CurrentUser) => void
}
export function useAuthenti(props: HookProps): CurrentUser | false {
const { onLogin } = props
const { isAuthenticated, currentUser } = useAuth()
const wasAuthenticated = usePrevious(isAuthenticated);
const [currentUserOrFalse, setCurrentUserOrFalse] = useState<CurrentUser | false>(false)
useEffect(() => {
console.log(`isAuthenticated CHANGED: ${wasAuthenticated} => ${isAuthenticated}`)
if (isAuthenticated) {
setCurrentUserOrFalse(currentUser)
if (wasAuthenticated === undefined) {
console.log(`false login 1`)
} else if (wasAuthenticated === false) {
console.log(`real login [MAKE API CALLS]`)
if (onLogin) {
console.log(`1. [inside] calling onlogin`)
onLogin?.(currentUser)
console.log(`4. [inside] called onLogin`)
}
} else if (wasAuthenticated === true) {
console.log(`false login 2`)
} else {
console.log(`false login 3`)
}
} else {
setCurrentUserOrFalse(false)
if (wasAuthenticated === undefined) {
console.log(`false logout 1`)
} else if (wasAuthenticated === false) {
console.log(`false logout 2`)
} else if (wasAuthenticated === true) {
console.log(`real logout [MAKE API CALLS]`)
} else {
console.log(`false logout 3`)
}
}
}, [isAuthenticated])
return currentUserOrFalse
}
and I am using this hook as follows:
export function Initialize({ children }: ComponentProps) {
const [getMyData, { loading: loading_MyData, data: data_MyData }] = useLazyQuery(MY_DATA_QUERY)
const [getAllPosts, { loading: loading_AllPosts, data: data_AllPosts }] = useLazyQuery(ALL_POSTS_QUERY)
useAuthenti({
onLogin: (currentUser: CurrentUser) => {
console.log(`2. [outside] onLogin start`)
getMyData()
getAllPosts()
console.log(`3. [outside] onLogin done`)
},
})
useEffect(() => {
if (data_MyData && data_AllPosts) {
console.log(data_MyData)
console.log(data_AllPosts)
}
}, [data_MyData, data_AllPosts])
return (
<>
{children}
</>
)
}
The Problem:
In the above usage, as you can see i am providing onLogin
function prop to the useAuthenti
custom hook. Because i want that function to run ON LOGIN and make the api calls in it.
However, the code isn't working as expected every time. sometimes it is working, other times it's not. (also weird.. seems like a race condition?)
When it's not working, I don't see any console logs (hinting the onLogin was never called), BUT, in networks tab I DO see calls being made by createHttpLink.js:100. it's failing also. During this case (when it doesn't work as expected), i see user login call succeeding BUT redwood js' getCurrentUser call isn't made. (you can ignore the redwoodjs part if you're unfamiliar with redwood js, just focus on the hook part) How are the apis that are inside onLogin running without any console logs surrounding it?
Networks Tab Debug
The red box shows attempt one at login (doesn't work) The green box shows attempt two at login (works)
Additional Note:
Apparently, only passing the functions as arguments is causing them to run EVEN if I don't run them inside the hook. Why are the functions passed as arguments to hook running on their own?
Is the way I am making a custom React Hook wrong? What am I doing wrong here if anyone could please let me know.
Is there a race condition as sometimes it'w working and sometimes it's not?
How do I proceed?
I don't understand why, but writing a custom hook to replace
useLazyQuery
that returns aPromise
fixed all the problems.This Github thread helped: https://github.com/apollographql/react-apollo/issues/3499
Particularly this answer: https://github.com/apollographql/react-apollo/issues/3499#issuecomment-539346982
To which I extended to support Typescript and States like
loading
error
anddata
. (his version isuseLazyQuery
, extended hook isuseLazyQueryModded
)useLazyQueryModded.tsx
If anyone can explain why
useLazyQuery
wasn't returning a Promise or why even when it didn't code worked sometime, I'd be relieved of this itch.