How do I use next-auth within a nextjs component? - reactjs

I'm trying too split my application into multiple components, and I'm trying to create a dashboard component, however, how do I go about implementing next-auth within the component, previously I had getServerSideProps but as this is a component I can't do that.
Here is my component
import {
SearchIcon,
BellIcon,
UserCircleIcon,
ChevronDownIcon,
UserIcon,
LogoutIcon
} from '#heroicons/react/outline';
import { signOut } from 'next-auth/client';
export default function Navigation(...session) {
return (
<>
<div className="py-6 px-8 lg:h-16 lg:flex justify-between items-center bg-blue-700 text-white">
<div className="flex-1">
<div className="lg:pr-4 lg:py-4">
<label htmlFor="search" className="sr-only">
Search
</label>
<div className="relative">
<div className="pointer-events-none absolute inset-y-0 left-0 pl-3 flex items-center">
<SearchIcon className="h-5 w-5 text-white" aria-hidden="true" />
</div>
<input
id="search"
name="search"
className="block w-full bg-blue-800 border border-blue-800 rounded-md py-2 pl-10 pr-3 text-sm placeholder-white focus:outline-none focus:ring-1 focus:ring-white focus:border-white sm:text-sm"
placeholder="Search"
type="search"
/>
</div>
</div>
</div>
<div className="relative flex items-center justify-center mt-8 lg:mt-0">
<div className="relative mr-6">
<BellIcon className="w-5" />
<small className="text-xs absolute -top-1 -right-2 -mt-2 bg-red-500 rounded-full py-0.5 px-1.5">
1
</small>
</div>
<details className="relative">
<summary className="flex items-center">
<UserCircleIcon className="w-5 mr-2" />
<h2>
{session.user.firstname} {session.user.lastname}
</h2>
<ChevronDownIcon
className="ml-2 flex-shrink-0 h-4 w-4 text-blue-200"
aria-hidden="true"
/>
</summary>
<div className="w-full mt-4 pb-4 absolute shadow-lg flex flex-col justify-center px-2 space-y-1 bg-blue-700">
<Link href="/dashboard/myprofile">
<a className="bg-blue-800 text-white hover:bg-blue-600 flex items-center px-2 py-2 text-sm font-medium rounded-md">
<UserIcon className="w-5 h-5 mr-2" />
Profile
</a>
</Link>
<Link href="#">
<a
onClick={signOut}
className="bg-blue-800 text-white hover:bg-blue-600 flex items-center px-2 py-2 text-sm font-medium rounded-md">
<LogoutIcon className="w-5 h-5 mr-2" />
Sign out
</a>
</Link>
</div>
</details>
</div>
</div>
</>
);
}
Currently what I'm getting is the following
TypeError: Cannot read properties of undefined (reading 'firstname')
edit #1
Here is my _app.js [UPDATED]
import Head from 'next/head';
import { Provider } from 'next-auth/client';
// assets
import '../styles/global.css';
import '../javascripts/app.js';
// components
import Footer from './components/Footer';
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<meta name="theme-color" content="#1E40AF" />
</Head>
<section className="flex flex-col min-h-screen">
<Provider session={pageProps.session}>
<Component {...pageProps} className="flex-1" />
</Provider>
</section>
<Footer />
</>
);
}
export default MyApp;

To fix this I did the following
_app.js - Now using provider to share the session across all pages.
import Head from 'next/head';
import { Provider } from 'next-auth/client';
// assets
import '../styles/global.css';
import '../javascripts/app.js';
// components
import Footer from './components/Footer';
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<meta name="theme-color" content="#1E40AF" />
</Head>
<section className="flex flex-col min-h-screen">
<Provider session={pageProps.session}>
<Component {...pageProps} className="flex-1" />
</Provider>
</section>
<Footer />
</>
);
}
export default MyApp;
and for the component I now have the following
import {
SearchIcon,
BellIcon,
UserCircleIcon,
ChevronDownIcon,
UserIcon,
LogoutIcon
} from '#heroicons/react/outline';
import { useSession, signOut } from 'next-auth/client';
import Link from 'next/link';
export default function Navigation() {
const [session, loading] = useSession()
return (
<>
<div className="py-6 px-8 lg:h-16 lg:flex justify-between items-center bg-blue-700 text-white">
<div className="flex-1">
<div className="lg:pr-4 lg:py-4">
<label htmlFor="search" className="sr-only">
Search
</label>
<div className="relative">
<div className="pointer-events-none absolute inset-y-0 left-0 pl-3 flex items-center">
<SearchIcon className="h-5 w-5 text-white" aria-hidden="true" />
</div>
<input
id="search"
name="search"
className="block w-full bg-blue-800 border border-blue-800 rounded-md py-2 pl-10 pr-3 text-sm placeholder-white focus:outline-none focus:ring-1 focus:ring-white focus:border-white sm:text-sm"
placeholder="Search"
type="search"
/>
</div>
</div>
</div>
<div className="relative flex items-center justify-center mt-8 lg:mt-0">
<div className="relative mr-6">
<BellIcon className="w-5" />
<small className="text-xs absolute -top-1 -right-2 -mt-2 bg-red-500 rounded-full py-0.5 px-1.5">
1
</small>
</div>
<details className="relative">
<summary className="flex items-center">
<UserCircleIcon className="w-5 mr-2" />
{ session &&
<h2>
{session.firstname} { session.lastname}
</h2>
}
<ChevronDownIcon
className="ml-2 flex-shrink-0 h-4 w-4 text-blue-200"
aria-hidden="true"
/>
</summary>
<div className="w-full mt-4 pb-4 absolute shadow-lg flex flex-col justify-center px-2 space-y-1 bg-blue-700">
<Link href="/dashboard/myprofile">
<a className="bg-blue-800 text-white hover:bg-blue-600 flex items-center px-2 py-2 text-sm font-medium rounded-md">
<UserIcon className="w-5 h-5 mr-2" />
Profile
</a>
</Link>
<Link href="#">
<a
onClick={signOut}
className="bg-blue-800 text-white hover:bg-blue-600 flex items-center px-2 py-2 text-sm font-medium rounded-md">
<LogoutIcon className="w-5 h-5 mr-2" />
Sign out
</a>
</Link>
</div>
</details>
</div>
</div>
</>
);
}

Related

Using tailwindcss flowbite react Navbar.Toggle hamburger menu Does not work properly

As I mention in my title Using tailwindcss and flowbite react Navbar. Toggle hamburger menu does not work properly.
I'm using flowbite react navbar component plugin for tailwindcss and got this weird hamburger menu with words in it. It supposes to be an icon instead of an Open main menu with an icon. Please refer to the code and image below for info.
Anyone had a solution for this?
Here is my code:
<Navbar
fluid={true}
rounded={false}>
<Navbar.Brand className='container flex flex-wrap items-center justify-between mx-auto'>
<Link
to='/'
className='flex items-center'>
<img
src={Logo}
className='h-6 mr-3 sm:h-9'
alt='Flowbite Logo'
/>
<span className='self-center text-xl font-semibold whitespace-nowrap dark:text-white'>
Logo here
</span>
</Link>
<div className='hidden lg:block'>
<Link
className='dark:text-white p-5'
to='/'>
Home
</Link>
<Link
className='dark:text-white p-5'
to='/academy'>
Academy
</Link>
<Link
className='dark:text-white p-5'
to='/signal'>
Signal
</Link>
</div>
<div className='flex md:order-2'>
{theme === 'dark' ? (
<button
onClick={handleThemeSwitch}
type='button'
className='text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-3 md:mr-0 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800'>
Light Mode
</button>
) : (
<button
onClick={handleThemeSwitch}
type='button'
className='text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-3 md:mr-0 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800'>
Dark Mode
</button>
)}
</div>
<Navbar.Toggle />
<Navbar.Collapse className='p-5'>
<Link to='/'>
<Navbar.Link>Home</Navbar.Link>
</Link>
<Link to='/academy'>
<Navbar.Link>Academy</Navbar.Link>
</Link>
<Link to='/signal'>
<Navbar.Link>Signal Group</Navbar.Link>
</Link>
</Navbar.Collapse>
</Navbar.Brand>
</Navbar>
Nvm i will just revamp the whole thing manually instead using their navbar toggle
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { Navbar } from 'flowbite-react';
import Logo from '../assets/logo.svg';
import { BsMenuUp } from 'react-icons/bs';
import { IconContext } from 'react-icons';
const Navigation = () => {
const [theme, setTheme] = useState('light');
const [toggle, setToggle] = useState(false);
useEffect(() => {
if (theme === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, [theme]);
const handleThemeSwitch = () => {
setTheme(theme === 'dark' ? 'light' : 'dark');
};
return (
<Navbar fluid={true} rounded={false}>
<Navbar.Brand className="container flex flex-wrap items-center justify-between mx-auto">
<Link to="/" className="flex items-center">
<img src={Logo} className="h-6 mx-3 sm:h-9" alt="Flowbite Logo" />
<span className="self-center text-xl font-semibold whitespace-nowrap dark:text-white">
Some Logo Here
</span>
</Link>
<div className="hidden lg:block">
<Link className="dark:text-white p-5" to="/">
Home
</Link>
<Link className="dark:text-white p-5" to="/academy">
Academy
</Link>
<Link className="dark:text-white p-5" to="/signal">
About
</Link>
</div>
<div className="flex md:order-2">
{theme === 'dark' ? (
<button
onClick={handleThemeSwitch}
type="button"
className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-3 md:mr-0 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
>
Light Mode
</button>
) : (
<button
onClick={handleThemeSwitch}
type="button"
className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-3 md:mr-0 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
>
Dark Mode
</button>
)}
<div className="lg:hidden flex align-middle justify-center p-1 text-gray-800 dark:text-white">
<IconContext.Provider value={{ size: 30 }}>
<BsMenuUp onClick={(e) => setToggle(!toggle)} />
</IconContext.Provider>
</div>
</div>
</Navbar.Brand>
{toggle ? (
<div className="p-2 m-5 bg-white dark:bg-gray-600 rounded-lg dark:text-white w-full">
<div>
<Navbar.Link as={Link} to="/">
Home
</Navbar.Link>
<Navbar.Link as={Link} to="/academy">
Academy
</Navbar.Link>
<Navbar.Link as={Link}>About</Navbar.Link>
</div>
</div>
) : null}
</Navbar>
);
};
export default Navigation;

<a> cannot appear as a descendant of <a>. Nextjs

I kept getting this error: Error: Hydration failed because the initial UI does not match what was rendered on the server.
Here is my code:
import { Fragment } from "react";
import { Menu, Transition } from "#headlessui/react";
import { ChevronDownIcon } from "#heroicons/react/20/solid";
import React from "react";
import { BsFillRecordCircleFill, BsFunnel } from "react-icons/bs";
import { FiCircle } from "react-icons/fi";
import Link from "next/link";
function classNames(...classes) {
return classes.filter(Boolean).join(" ");
}
const Filter = () => {
return (
<div className="">
{/* Menu */}
<Menu as="div" className="relative inline-block text-left">
<div>
<Menu.Button className="inline-flex w-full justify-center rounded-md border border-gray-300 bg-[#1F3C71] text-white px-4 py-3 text-sm font-medium shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-gray-100 gap-1 items-center">
<BsFunnel />
Filter
<p className="bg-white text-[#1F3C71] rounded-full w-5 h-5">2</p>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute z-10 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="py-1">
<div className="flex items-center justify-between py-2 px-5">
<p className="font-bold">Filters</p>
<p className="font-semibold text-[#1F3C71]">Apply</p>
</div>
<hr />
<Menu.Item>
{({ active }) => (
<main className="flex items-center">
<BsFillRecordCircleFill className="ml-6 text-[#1F3C71]" />
<Link
href="#"
className={classNames(
active ? "bg-white text-gray-900" : "text-gray-700",
"block px-4 py-2 text-sm"
)}
>
Status
</Link>
</main>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<main className="flex items-center">
<FiCircle className="ml-6" />
<Link
href="#"
className={classNames(
active ? "bg-white text-gray-900" : "text-gray-700",
"block px-4 py-2 text-sm"
)}
>
Email
</Link>
</main>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<main className="flex items-center">
<BsFillRecordCircleFill className="ml-6 text-[#1F3C71]" />
<Link
href="#"
className={classNames(
active ? "bg-white text-gray-900" : "text-gray-700",
"block px-4 py-2 text-sm"
)}
>
Data Range
</Link>
</main>
)}
</Menu.Item>
</div>
</Menu.Items>
</Transition>
</Menu>
</div>
);
};
export default Filter;
I try to change the Link tag but still having the issue.
I also got this error as well: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.

implementing session in nextauth is disabling the model

I'm trying to implement google authentication on my nextjs app. i'm implementing this in my layout component,
when i use user = null and isLoadingUser the button is loading fine but when changed to user = session?.user and isloadingUser = 'loading' the button is set being set to disabled?
Layout coponent code:
import { Fragment, useState } from 'react';
import { useRouter } from 'next/router';
import Head from 'next/head';
import Link from 'next/link';
import Image from 'next/image';
import PropTypes from 'prop-types';
import AuthModal from './AuthModal';
import { Menu, Transition } from '#headlessui/react';
import {
HeartIcon,
HomeIcon,
LogoutIcon,
PlusIcon,
SparklesIcon,
UserIcon,
} from '#heroicons/react/outline';
import { ChevronDownIcon } from '#heroicons/react/solid';
import { useSession, signOut } from 'next-auth/react';
const menuItems = [
{
label: 'List a new home',
icon: PlusIcon,
href: '/list',
},
{
label: 'My homes',
icon: HomeIcon,
href: '/homes',
},
{
label: 'Favorites',
icon: HeartIcon,
href: '/favorites',
},
{
label: 'Logout',
icon: LogoutIcon,
onClick: signOut,
},
];
const Layout = ({ children = null }) => {
const router = useRouter();
const { data: session, status } = useSession()
const [showModal, setShowModal] = useState(false);
const user = session?.user
const isLoadingUser = 'loading';
const openModal = () => setShowModal(true);
const closeModal = () => setShowModal(false);
return (
<>
<Head>
<title>SupaVacation | The Modern Dev</title>
<meta
name="title"
content="Learn how to Build a Fullstack App with Next.js, PlanetScale & Prisma | The Modern Dev"
/>
<link rel="icon" href="/favicon.ico" />
</Head>
<div className="min-h-screen flex flex-col">
<header className="h-16 w-full shadow-md">
<div className="h-full container mx-auto">
<div className="h-full px-4 flex justify-between items-center space-x-4">
<Link href="/">
<a className="flex items-center space-x-1">
<SparklesIcon className="shrink-0 w-8 h-8 text-rose-500" />
<span className="text-xl font-semibold tracking-wide">
Supa<span className="text-rose-600">Vacation</span>
</span>
</a>
</Link>
<div className="flex items-center space-x-4">
<Link href="/create">
<a className="hidden sm:block hover:bg-gray-200 transition px-3 py-1 rounded-md">
List your home
</a>
</Link>
{isLoadingUser ? (
<div className="h-8 w-[75px] bg-gray-200 animate-pulse rounded-md" />
) : user ? (
<Menu as="div" className="relative z-50">
<Menu.Button className="flex items-center space-x-px group">
<div className="shrink-0 flex items-center justify-center rounded-full overflow-hidden relative bg-gray-200 w-9 h-9">
{user?.image ? (
<Image
src={user?.image}
alt={user?.name || 'Avatar'}
layout="fill"
/>
) : (
<UserIcon className="text-gray-400 w-6 h-6" />
)}
</div>
<ChevronDownIcon className="w-5 h-5 shrink-0 text-gray-500 group-hover:text-current" />
</Menu.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 w-72 overflow-hidden mt-1 divide-y divide-gray-100 origin-top-right bg-white rounded-md shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="flex items-center space-x-2 py-4 px-4 mb-2">
<div className="shrink-0 flex items-center justify-center rounded-full overflow-hidden relative bg-gray-200 w-9 h-9">
{user?.image ? (
<Image
src={user?.image}
alt={user?.name || 'Avatar'}
layout="fill"
/>
) : (
<UserIcon className="text-gray-400 w-6 h-6" />
)}
</div>
<div className="flex flex-col truncate">
<span>{user?.name}</span>
<span className="text-sm text-gray-500">
{user?.email}
</span>
</div>
</div>
<div className="py-2">
{menuItems.map(
({ label, href, onClick, icon: Icon }) => (
<div
key={label}
className="px-2 last:border-t last:pt-2 last:mt-2"
>
<Menu.Item>
{href ? (
<Link href={href}>
<a className="flex items-center space-x-2 py-2 px-4 rounded-md hover:bg-gray-100">
<Icon className="w-5 h-5 shrink-0 text-gray-500" />
<span>{label}</span>
</a>
</Link>
) : (
<button
className="w-full flex items-center space-x-2 py-2 px-4 rounded-md hover:bg-gray-100"
onClick={onClick}
>
<Icon className="w-5 h-5 shrink-0 text-gray-500" />
<span>{label}</span>
</button>
)}
</Menu.Item>
</div>
)
)}
</div>
</Menu.Items>
</Transition>
</Menu>
) : (
<button
type="button"
onClick={openModal}
className="ml-4 px-4 py-1 rounded-md bg-rose-600 hover:bg-rose-500 focus:outline-none focus:ring-4 focus:ring-rose-500 focus:ring-opacity-50 text-white transition"
>
Log in
</button>
)}
</div>
</div>
</div>
</header>
<main className="flex-grow container mx-auto">
<div className="px-4 py-12">
{typeof children === 'function' ? children(openModal) : children}
</div>
</main>
<AuthModal show={showModal} onClose={closeModal} />
</div>
</>
);
};
Layout.propTypes = {
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
};
export default Layout;

Match Params Id not working, how to fix it?

So i'm trying to access the id of my ypdate, and the example code i fould uses Match, I don't think the newer version of React likes it. I think I need to implement useParams(), however I new to coding and i'm not sure how to. Here is my code with Match params
import { useEffect } from "react";
import { Redirect } from "react-router-dom";
import { PlusCircleIcon, BookOpenIcon } from "#heroicons/react/solid";
import { useFormik } from "formik";
import * as Yup from "yup";
import { useDispatch, useSelector } from "react-redux";
import {
fetchCategoryAction,
updateCategoriesAction,
deleteCategoriesAction,
} from "../../redux/slices/category/categorySlice";
//Form schema
const formSchema = Yup.object({
title: Yup.string().required("Title is required"),
});
const UpdateCategory = ({
match: {
params: { id },
},
}) => {
const dispatch = useDispatch();
//fetch single category
useEffect(() => {
dispatch(fetchCategoryAction(id));
}, []);
//get data from store
const state = useSelector(state => state?.category);
const { loading, appErr, serverErr, category, isEdited, isDeleted } = state;
//formik
const formik = useFormik({
enableReinitialize: true,
initialValues: {
title: category?.title,
},
onSubmit: values => {
//build up the date for update
//dispath the action
dispatch(updateCategoriesAction({ title: values.title, id }));
},
validationSchema: formSchema,
});
//redirect
if (isEdited || isDeleted) return <Redirect to="/category-list" />;
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8">
<div>
<BookOpenIcon className="mx-auto h-12 w-auto" />
<h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
Update Category
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
<p className="font-medium text-indigo-600 hover:text-indigo-500">
These are the categories user will select when creating a post
</p>
{/* Display err */}
<div>
{appErr || serverErr ? (
<h2 className="text-red-500 text-center text-lg">
{serverErr} {appErr}
</h2>
) : null}
</div>
</p>
</div>
{/* Form */}
<form onSubmit={formik.handleSubmit} className="mt-8 space-y-6">
<input type="hidden" name="remember" defaultValue="true" />
<div className="rounded-md shadow-sm -space-y-px">
<div>
<label htmlFor="email-address" className="sr-only">
Name
</label>
{/* Title */}
<input
value={formik.values.title}
onChange={formik.handleChange("title")}
onBlur={formik.handleBlur("title")}
type="text"
autoComplete="text"
className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 text-center focus:z-10 sm:text-sm"
placeholder="New Category"
/>
<div className="text-red-400 mb-2">
{formik.touched.title && formik.errors.title}
</div>
</div>
</div>
<div>
<div>
{/* Submit */}
{loading ? (
<button
disabled
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-gray-600 "
>
<span className="absolute left-0 inset-y-0 flex items-center pl-3">
<PlusCircleIcon
className="h-5 w-5 text-yellow-500 group-hover:text-indigo-400"
aria-hidden="true"
/>
</span>
Loading please wait...
</button>
) : (
<>
<button
type="submit"
className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-yellow-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
<span className="absolute left-0 inset-y-0 flex items-center pl-3">
<PlusCircleIcon
className="h-5 w-5 text-yellow-500 group-hover:text-indigo-400"
aria-hidden="true"
/>
</span>
Update Category
</button>
<button
onClick={() => dispatch(deleteCategoriesAction(id))}
type="submit"
className="group mt-2 relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-red-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Delete Category
</button>
</>
)}
</div>
</div>
</form>
</div>
</div>
);
};
export default UpdateCategory;
Nothing renders to the screen with it in there I take it out and I do, however the functionality i need for updating isn't there. Any help would be greatly appreciated.

React send state after login to other component to change html

I'm working on a project ( symfony API ) and ReactJs front-end,
I have a login page which is connected to the api when i get the 201 http response i redirect the user to '/' and set the token in the localstorage but i have an issue in my navbar i have 2 buttons 1 login and the other is for register to redirect to both pages.
When i log in i want to dynamically change the header to a dropdown with user informations and links to user settings etc ... Do you have any ideas ?
Here is the code of Navbar component :
import React, {useEffect, useState} from 'react';
import Dropdown from "./Dropdown";
import {Link} from "react-router-dom";
const Navbar = () => {
const [isloggedin, setIsloggedin] = useState(false);
useEffect(() => {
if (!localStorage.getItem('auth')) {
setIsloggedin(false);
} else {
setIsloggedin(true);
}
}, [])
const ToggleMobileMenu = () => {
const mobile_menu = document.getElementById('mobile-menu')
if (mobile_menu.classList.contains('mobile-hidden')){
mobile_menu.classList.remove('mobile-hidden')
} else {
mobile_menu.classList.add('mobile-hidden')
}
}
return (
<nav className="bg-gray-800 w-full p-4">
<div className="max-w-7xl mx-auto px-2 sm:px-6 lg:px-8">
<div className="relative flex items-center justify-between h-16">
<div className="absolute inset-y-0 left-0 flex items-center sm:hidden">
<button type="button"
className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
aria-controls="mobile-menu" aria-expanded="false"
onClick={ToggleMobileMenu}
>
<span className="sr-only">Open main menu</span>
<svg className="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M4 6h16M4 12h16M4 18h16"/>
</svg>
<svg className="hidden h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<div className="flex-1 flex items-center justify-center sm:items-stretch sm:justify-start">
<div className="flex-shrink-0 flex items-center">
<Link to={'/'}><img src={'./assets/logo.png'} width={'150'} alt={''}/></Link>
</div>
<div className="hidden sm:flex items-center sm:ml-6 flex">
<div className="flex space-x-4 items-center">
<Link to={'/'}
className="bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium"
aria-current="page">Accueil</Link>
<Link to={'/products'}
className="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Nos
Produits
</Link>
<Link to={'/delivery'}
className="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Livraison</Link>
<Link to={'/contact'}
className="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Contact</Link>
</div>
</div>
</div>
<div
className="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0">
<div className={'ml-3 relative'}>
{isloggedin ? (
<div className={'ml-3 relative'}>
<Dropdown/>
</div>
) : (
<div className={'ml-3 relative flex'}>
<Link to={'/login'}
className={'hidden md:flex mr-5 text-white bg-red-200 p-3 rounded'}>Se
connecter</Link>
<Link to={'/register'}
className={'hidden md:flex mr-5 text-white bg-red-200 p-3 rounded'}>S'inscrire</Link>
</div>
)}
</div>
</div>
</div>
</div>
<div className="sm:hidden mobile-hidden" id="mobile-menu">
<div className="px-2 pt-2 pb-3 space-y-1">
<Link to={'/'}
className="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium mobile-link" onClick={ToggleMobileMenu}>Accueil</Link>
<Link to={'/products'}
className="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium mobile-link" onClick={ToggleMobileMenu}>Nos Produits</Link>
<Link to={'/delivery'}
className="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium mobile-link" onClick={ToggleMobileMenu}>Livraison</Link>
<Link to={'/contact'}
className="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium mobile-link" onClick={ToggleMobileMenu}>Contact</Link>
{isloggedin ? (
<Link to={'/account'}
className="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium mobile-link" onClick={ToggleMobileMenu}>Mon compte</Link>
) : (
<div>
<Link to={'/login'}
className="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium mobile-link" onClick={ToggleMobileMenu}>Login</Link>
<Link to={'/register'}
className="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium mobile-link" onClick={ToggleMobileMenu}>Register</Link>
</div>
)}
</div>
</div>
</nav>
)
}
export default Navbar;
And here is the Login component :
import React, {useState} from "react";
import { useHistory } from 'react-router-dom';
import axios from "axios";
const Login = () => {
let history = useHistory()
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [success, setSuccess] = useState('');
const LoginHandler = () => {
axios.post('http://127.0.0.1:8000/api/login', {
'username': username,
'password': password
})
.then((r) => {
console.log(r)
setError('')
setSuccess('Vous etes connecté')
setTimeout(() => {
localStorage.setItem('auth', r.data.token)
history.push('/', {auth: true})
}, 3000)
}).catch((error) => {
setError("Username ou mot de passe éroné");
})
}
return (
<div>
<div className={'text-center font-semibold mt-2'}>
{error ? (<p className={'text-red-500'}>{error}</p>) : ( '' )}
{success ? (<p className={'text-green-500'}>{success}</p>) : ( '' )}
</div>
<div className="divide-y divide-gray-200">
<div className="py-8 text-base leading-6 space-y-4 text-gray-700 sm:text-lg sm:leading-7">
<div className="relative">
<input autoComplete="off" id="username" name="username" type="text"
className="peer placeholder-transparent h-10 w-full border-b-2 border-gray-300 text-gray-900 focus:outline-none focus:borer-rose-600"
placeholder="Username"
onChange={(e) => setUsername(e.target.value)}
/>
<label htmlFor="username"
className="absolute left-0 -top-3.5 text-gray-600 text-sm peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-440 peer-placeholder-shown:top-2 transition-all peer-focus:-top-3.5 peer-focus:text-gray-600 peer-focus:text-sm">username</label>
</div>
<div className="relative">
<input autoComplete="off" id="password" name="password" type="password"
className="peer placeholder-transparent h-10 w-full border-b-2 border-gray-300 text-gray-900 focus:outline-none focus:borer-rose-600"
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
/>
<label htmlFor="password"
className="absolute left-0 -top-3.5 text-gray-600 text-sm peer-placeholder-shown:text-base peer-placeholder-shown:text-gray-440 peer-placeholder-shown:top-2 transition-all peer-focus:-top-3.5 peer-focus:text-gray-600 peer-focus:text-sm">Password</label>
</div>
<div className="flex justify-center pt-6">
<button onClick={LoginHandler} className="bg-blue-500 text-white rounded-md px-2 py-1">Se
connecter
</button>
</div>
</div>
</div>
</div>
)
}
export default Login;
Right now your useEffect hook:
useEffect(() => {
if (!localStorage.getItem('auth')) {
setIsloggedin(false);
} else {
setIsloggedin(true);
}
}, [])
... is only triggered on the initial render of the navbar.
You have two options - I'd strongly advice to use the second one.
Option 1: The useEffect Hook has a dependency list (the second parameter passed to useEffect. Whenever any value inside that dependency list changes, the code is run again. So in order for the navbar to update, you have to add that localStorage "state" to the list. However, I don't know whether this will work that well - this is not the "react way" of doing things.
Option 2: Change your State Hierarchy. Right now your hierarchy looks like this:
<Navbar>
<Login>
</Navbar>
In react you should store state with useState (you can add auth to the localStorage in addition to that). That state should be stored in the highest order component that is affected by the state.
Solution: useState for a user object inside Navbar. Pass the setUserState function to the login component and trigger setUserState on successful login (next to localStorage.setItem). Add that userState to the dependency list of the useEffect hook mentioned above.

Resources