Reputation: 118
I am new to NextJS and I've been trying to create a way after the direct google signIn with NextAuth, that if the user does not have an username, then he needs to create one. After he presses the 'Save' button it triggers onSubmit function:
async function onSubmit(values) {
const options = {
method: "POST",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(values)
}
await fetch('/api/auth/first_time', options).then(res => {
if (res.status == 201) {
reloadSession();
} else return res.json();
}).then(async function (data) {
if (data) setError({ status: true, message: data.message });
})
}
This function sends normally and saves in the database according to the condition, then reload the session to get the new values of username, but after this function I get the error:
Unhandled Runtime Error
Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
Call Stack
renderWithHooks
node_modules\react-dom\cjs\react-dom.development.js (16381:0)
updateFunctionComponent
node_modules\react-dom\cjs\react-dom.development.js (19588:0)
beginWork
node_modules\react-dom\cjs\react-dom.development.js (21601:0)
HTMLUnknownElement.callCallback
node_modules\react-dom\cjs\react-dom.development.js (4164:0)
Object.invokeGuardedCallbackDev
node_modules\react-dom\cjs\react-dom.development.js (4213:0)
invokeGuardedCallback
node_modules\react-dom\cjs\react-dom.development.js (4277:0)
beginWork$1
node_modules\react-dom\cjs\react-dom.development.js (27451:0)
performUnitOfWork
node_modules\react-dom\cjs\react-dom.development.js (26557:0)
workLoopSync
node_modules\react-dom\cjs\react-dom.development.js (26466:0)
renderRootSync
node_modules\react-dom\cjs\react-dom.development.js (26434:0)
performConcurrentWorkOnRoot
node_modules\react-dom\cjs\react-dom.development.js (25738:0)
workLoop
node_modules\scheduler\cjs\scheduler.development.js (266:0)
flushWork
node_modules\scheduler\cjs\scheduler.development.js (239:0)
MessagePort.performWorkUntilDeadline
node_modules\scheduler\cjs\scheduler.development.js (533:0)
Edit: After I press the 'Save' button and refresh the page, the page works normally and the session works fine.
Where I think is the problem
export default function Home() {
const { data: session } = useSession()
session.user = session.user || {}
Object.keys(session.user).length === 0 ? session.user = { email: session.email, name: session.name, image: session.picture, username: session.username } : session.user = session.user
const isUsernameDefined = session.user.username !== '' && session.user.username !== undefined
function handleSignOut() {
signOut()
}
return session ? isUsernameDefined ? User({ session, handleSignOut }) : FirstTime({ session }) : Guest()
}
Whole code
import Head from 'next/head'
import styles from '../styles/Home.module.css'
import Link from 'next/link'
import { useSession, getSession, signOut } from 'next-auth/react'
import { useFormik } from 'formik';
import { first_time_validate } from '../lib/validate';
import { HiOutlineUser } from "react-icons/hi"
import { useState } from 'react';
export default function Home() {
const { data: session } = useSession()
session.user = session.user || {}
Object.keys(session.user).length === 0 ? session.user = { email: session.email, name: session.name, image: session.picture, username: session.username } : session.user = session.user
const isUsernameDefined = session.user.username !== '' && session.user.username !== undefined
function handleSignOut() {
signOut()
}
return session ? isUsernameDefined ? User({ session, handleSignOut }) : FirstTime({ session }) : Guest()
}
function FirstTime({ session, definedUsername }) {
const [error, setError] = useState({ status: false, message: '' });
const formik = useFormik({
initialValues: {
username: '',
email: session.user.email
},
validate: first_time_validate,
onSubmit
})
async function onSubmit(values) {
const options = {
method: "POST",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(values)
}
await fetch('/api/auth/first_time', options).then(res => {
if (res.status == 201) {
reloadSession();
} else return res.json();
}).then(async function (data) {
if (data) setError({ status: true, message: data.message });
})
}
return (
<div className={styles.container}>
<Head>
<title>Home Page</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className="container mx-auto text-center py-20">
<h3 className='text-4xl font-bold'>Welcome {session?.user?.name ? session.user.name : <></>}</h3>
<h5 className='text-2xl'>Seems like its your first time here, please enter an username</h5>
<div className='flex justify-center'>
<form className='flex flex-col gap-5' onSubmit={(e) => { e.preventDefault(); formik.handleSubmit(e) }}>
<div className={`${styles.input_group} ${formik.errors.username && formik.touched.username ? 'border-rose-600' : ''}`}>
<input type='text' placeholder='Username' name='Username' className={styles.input_text} {...formik.getFieldProps('username')} />
<span className='icon flex items-center px-4'><HiOutlineUser size={25} /></span>
</div>
<input type='hidden' name='email' value={session.user.email} {...formik.getFieldProps('email')} />
{error.status ? <span className='text-rose-500'>{error.message}</span> : <></>}
<div className='input-button'>
<button type='submit' className={styles.button}>
Save
</button>
</div>
</form>
</div>
</main>
</div>
)
}
// Guest
function Guest() {
return (
<div className={styles.container}>
<Head>
<title>Home Page</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className="container mx-auto text-center py-20">
<h3 className='text-4xl font-bold'>Guest's Home</h3>
<div className='flex justify-center'>
<Link href={'/login'} className='mt-5 px-10 py-1 rounded-sm bg-indigo-500 text-gray-50'>Sign In</Link>
</div>
</main>
</div>
)
}
//Authorized User
function User({ session, handleSignOut }) {
return (
<div className={styles.container}>
<Head>
<title>Home Page</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className="container mx-auto text-center py-20">
<h3 className='text-4xl font-bold'>Home</h3>
<div className='details'>
{session.user.name ? <h5>{session.user.name}</h5> : <></>}
</div>
<div className='flex justify-center'>
<Link href={'/profile'} className='mt-5 px-10 py-1 rounded-sm bg-indigo-500 text-gray-50'>Profile</Link>
</div>
<div className='flex justify-center'>
<Link href={'/fit'} className='mt-5 px-10 py-1 rounded-sm bg-indigo-500 text-gray-50'>Fit</Link>
</div>
<div className='flex justify-center'>
<button onClick={handleSignOut} className='mt-5 px-10 py-1 rounded-sm bg-indigo-500 bg-gray-50'>Sign out</button>
</div>
</main>
</div>
)
}
export async function getServerSideProps({ req }) {
const session = await getSession({ req });
if (!session) return {
redirect: {
destination: '/login',
permanent: false
}
}
return {
props: { session }
}
}
const reloadSession = () => {
const event = new Event("visibilitychange");
document.dispatchEvent(event);
};
I have tried to change the way the return in Home works, but could not find a proper way that would not trigger this problem. I am very sorry if this problem is basic and I was too dumb to not see it.
Upvotes: 0
Views: 235
Reputation: 118
Thanks @koolskateguy89 for the tip, managed to fix the error, changing the usage of functions:
return session ? isUsernameDefined ? User({ session, handleSignOut }) : FirstTime({ session }) : Guest()
to JSX components like this:
return session ? isUsernameDefined ? <User session={session}/> : <FirstTime session={session}/> : <Guest/>
handling the props like this inside the component:
const FirstTime = (props) => {
const session = props.session
When using the previous line, User() FirstTime and Guest() were becoming hooks instead of components and thats where it was the problem.
Upvotes: 2