Reactjs hide div when click outside or click men - reactjs

I am creating language switcher component using tailwind css.
Language chooser is working fine, but I can't hide the Language when click outside of the .
Is there any react component for Language Switcher?
May I know how to hide the div click outside.
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { MyanmarFlag, EnglishFlag } from "../assets/icons/svg_icons";
const LanguageSelector = () => {
const [isOpen, setIsOpen] = useState(false);
const { t, i18n } = useTranslation();
const changeLanguage = (lng) => {
i18n.changeLanguage(lng);
};
return (
<div className="flex justify-end p-4">
<div className="relative">
<button
className="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2
bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2
focus:ring-offset-gray-100 focus:ring-primary"
onClick={() => setIsOpen(!isOpen)}
>
Select Language
</button>
{isOpen && (
<div
className="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg
bg-white ring-1 ring-black ring-opacity-5 divide-y divide-gray-100
focus:outline-none z-50"
>
<div className="py-1">
<a
className="group flex items-center px-4 py-2 text-sm text-gray-700
hover:bg-primary hover:text-white"
onClick={() => changeLanguage("my")}
>
<MyanmarFlag
className="mr-3 h-5 w-5 text-gray-400 group-hover:text-white"
aria-hidden="true"
></MyanmarFlag>
Myanmar
</a>
</div>
<div className="py-1">
<a
className="group flex items-center px-4 py-2 text-sm text-gray-700
hover:bg-primary hover:text-white"
onClick={() => changeLanguage("en")}
>
<EnglishFlag
className="mr-3 h-5 w-5 text-gray-400 group-hover:text-white"
aria-hidden="true"
></EnglishFlag>
English
</a>
</div>
</div>
)}
</div>
</div>
);
};
export default LanguageSelector;

You can use useRef hook and bind event listener to your div, then change your isOpen state.
See Detect click outside React component

Related

I'm using Tailwind in a React project. How do I successfully get the modal to fully go away after submitting? It seems that it's still greyed out

Here is my Signup.js
import React from "react";
import { useToken } from "./AuthenticateUser";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
export default function Signup() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [first_name, setFirst] = useState("");
const [last_name, setLast] = useState("");
const [zipcode, setZip] = useState("");
const [errorMessage, setErrorMessage] = useState("");
const navigate = useNavigate();
const [, , , signup] = useToken();
const modal = document.getElementById("signup");
const handleFormSubmit = async (e) => {
e.preventDefault();
if (formValidation() === false) {
return;
}
signup(first_name, last_name, email, zipcode, password);
setEmail("");
setPassword("");
setFirst("");
setLast("");
setZip("");
navigate("/me");
setErrorMessage("");
modal.classList.remove("show");
};
function formValidation() {
let blankInputs = 0;
if (email.length === 0) {
blankInputs++;
}
if (password.length === 0) {
blankInputs++;
}
if (first_name.length === 0) {
blankInputs++;
}
if (last_name.length === 0) {
blankInputs++;
}
if (zipcode.length === 0) {
blankInputs++;
}
if (blankInputs === 5) {
setErrorMessage("Form submission is completely blank.");
return false;
}
if (blankInputs > 1) {
setErrorMessage("Form has multiple blank inputs.");
return false;
}
if (!validateEmail()) {
setErrorMessage("Whoops! Email format is invalid.");
return false;
}
if (zipcode.length < 5) {
setErrorMessage("Whoops! Zipcode needs to be at least 5 characters");
return false;
}
if (!password) {
setErrorMessage("Whoops! Password is required.");
return false;
}
if (!first_name) {
setErrorMessage("Whoops! First name is required.");
return false;
}
if (!last_name) {
setErrorMessage("Whoops! Last name is required.");
return false;
}
return true;
}
function validateEmail() {
const regex =
/^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return regex.test(String(email).toLowerCase());
}
return (
<>
<div
className="modal fade fixed top-0 left-0 hidden w-full h-full outline-none overflow-x-hidden overflow-y-auto"
id="signup"
tabIndex="-1"
aria-labelledby="signupLabel"
aria-modal="true"
role="dialog"
>
<div className="modal-dialog modal-dialog-centered relative w-auto pointer-events-none">
<div className="modal-content border-none shadow-lg relative flex flex-col w-full pointer-events-auto bg-[#F0C797] bg-clip-padding rounded-md outline-none text-current">
<div className="modal-header p-6 mt-2 text-center">
<div className="flex justify-center items-center">
<h1 className="text-3xl font-bold mr-4">SIGN UP</h1>
<img src={require("../images/checklist.png")} width="50px" />
</div>
<svg
className="w-9 h-9 absolute top-3 right-2.5 text-black bg-transparent rounded-lg text-sm p-1.5 ml-auto inline-flex items-center hover:bg-[#FEF5ED] hover:text-white ease-linear transition-all duration-150 cursor-pointer"
fillRule="currentColor"
data-bs-dismiss="modal"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clipRule="evenodd"
></path>
</svg>
</div>
<div className="modal-body relative p-4">
<form
onSubmit={handleFormSubmit}
className="container max-w-sm mx-auto flex-1 flex flex-col items-center justify-center px-3"
>
<input
type="text"
className="block border border-grey-light w-full p-3 rounded mb-4 placeholder:text-sm"
placeholder="First Name"
onChange={(e) => setFirst(e.target.value)}
value={first_name}
/>
<input
type="text"
className="block border border-grey-light w-full p-3 rounded mb-4 placeholder:text-sm"
placeholder="Last Name"
onChange={(e) => setLast(e.target.value)}
value={last_name}
/>
<input
type="email"
className="block border border-grey-light w-full p-3 rounded mb-4 placeholder:text-sm"
placeholder="Email"
onChange={(e) => setEmail(e.target.value)}
value={email}
/>
<input
type="text"
className="block border border-grey-light w-full p-3 rounded mb-4 placeholder:text-sm"
placeholder="Zipcode"
onChange={(e) => setZip(e.target.value)}
value={zipcode}
/>
<input
type="password"
className="block border border-grey-light w-full p-3 rounded mb-4 placeholder:text-sm"
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
value={password}
/>
{errorMessage ? (
<div className="flex p-4 mb-4 text-sm text-red-700 border border-red-300 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400 dark:border-red-800 items-center">
<img
src={require("../images/warning.png")}
width="30px"
style={{ marginRight: "15px" }}
/>
{errorMessage}
</div>
) : null}
<div className="flex flex-col items-center justify-end p-3 border-solid border-slate-200 rounded-b">
<button
// data-bs-dismiss="modal"
className="bg-black text-white font-bold uppercase text-sm px-6 py-3 rounded inline-flex group items-center justify-center cursor-pointer"
>
<span className="absolute w-0 h-0 transition-all duration-300 ease-out bg-[#F0C797] group-hover:w-32 group-hover:h-24 opacity-10"></span>
Order up!
</button>
</div>
</form>
<div className="flex items-center">
<a
data-bs-toggle="modal"
data-bs-target="#login"
className="mb-6 mt-4 mx-auto text-black-500 background-transparent font-bold underline uppercase text-sm focus:outline-none ease-linear transition-all duration-150 hover:text-white cursor-pointer"
>
Already have an account?
</a>
</div>
</div>
</div>
</div>
</div>
</>
);
}
**
Here is my Nav.js**
mport { useState } from "react";
function Nav() {
let [nav, setNav] = useState(false);
// nav = false
function handleNav() {
setNav(!nav);
}
return (
<nav className="flex justify-between items-center bg-[#FDECA9] py-3">
<div className="mx-auto mr-25">
<a href="/">
<div className="flex space-x-1 tracking-[4px] text-xl font-semibold items-center">
<span>PLATE</span>
<img
src={require("./images/plate.png")}
className="h-9"
alt="PlateMate Logo"
/>
<span>MATE</span>
</div>
</a>
</div>
<div className="hidden md:flex items-center">
<button
type="button"
className="bg-[#BB5855] mx-0 rounded text-[#FDECA9] text-sm py-1 px-4 relative inline-flex group items-center justify-center cursor-pointer"
data-bs-toggle="modal"
data-bs-target="#signup"
>
<span className="absolute w-0 h-0 transition-all duration-300 ease-out bg-white rounded-full group-hover:w-32 group-hover:h-32 opacity-10"></span>
SIGNUP
</button>
<button
type="button"
className="text-[#BB5855] mx-6 rounded text-sm outline outline-offset-4 outline-2 py-0 px-4 relative font-semibold text-center no-underline transition-all duration-300 ease-in-out cursor-pointer hover:text-[#bb58557c] "
data-bs-toggle="modal"
data-bs-target="#login"
>
LOGIN
</button>
</div>
<div className="block md:hidden">
{/* Mobile Hamburger Icon */}
<button
onClick={handleNav}
className="inline-flex items-center p-2 ml-3 text-sm md:hidden "
>
<img src={require("../src/images/hamburger.png")} width="30px" />
</button>
{/* Dropdown menu */}
<div
className={
nav
? "block absolute right-0 z-10 mt-0 w-56 mr-2 origin-top-right rounded-md bg-[#BB5855] shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
: "hidden"
}
>
<ul className="py-1 text-sm text-gray-100 divide-y ">
<div>
<a
data-bs-toggle="modal"
data-bs-target="#signup"
className="block px-4 py-2 transition-all duration-300 ease-in-out cursor-pointer hover:text-black"
>
SIGNUP
</a>
</div>
<div>
<a
data-bs-toggle="modal"
data-bs-target="#login"
className="block px-4 py-2 transition-all duration-300 ease-in-out cursor-pointer hover:text-black"
>
LOGIN
</a>
</div>
</ul>
</div>
</div>
</nav>
);
}
export default Nav;
I tried researching everything online but I can't find anything with Tailwind, only Bootstrap. I'm expecting the modal to not dismiss when there is an error message but dismiss completely after signed up. It does that but my issue is the grey background not disappearing. I have nothing in my CSS files that would tie to it. I've also tried commenting everything out on all of my files to see if anything was triggering it, however, it still appears. Please help!
Your code shows that you aren't really using React in the way it's intended. You should me managing the state in React, and not manipulating the DOM manually.
A common pattern for removing a modal is to use a state variable to handle the visibility of the component.
const [modalOpen, setModalOpen] = useState(false);
Then around your modal, you can render it conditionally:
return (<>
{modalOpen && (<YourModelHere />)}
</>)
You can then hide/show your modal using the setModalOpen() method.

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.

why all my favorite button color change when i click on one of them? - ReactJs

I have an object called result, and I use map to loop the data inside result. I have a button in each data and its use to add the data to the database, after that the button color change. The problem is everytime I click the favorite button, all the button color in map got change too...
note: I m using if else to change the color (fa fa-heart and fa fa-heart-o)
import { Link } from "react-router-dom";
import { useState } from "react";
import 'font-awesome/css/font-awesome.min.css';
import "./App.css";
import DBRequest from "./DBRequest";
const AnimeList = ( { results } ) => {
const [favorite, setFavorite] = useState(false);
const FavoriteState = (mal_id) => {
var data_mal_id= {
mal_id: mal_id
}
if(favorite){
setFavorite(false);
DBRequest(`/api/favorite/delete/${mal_id}`, "DELETE", data_mal_id);
}else{
setFavorite(true);
DBRequest(`/api/favorite/post`, "POST", data_mal_id);
}
}
return (
<div className="anime-list flex overflow-auto no-scrollbar">
{ results && results.map((result) => (
<div className="anime-preview p-3" key={ result.mal_id }>
<div className="group w-48 h-72 relative cursor-none">
<img className="object-cover absolute h-full w-full z-10 filter group-hover:blur-sm group-hover:opacity-50" src={ result.image_url } alt="" />
<h2 className="absolute w-full top-0 text-left p-2 font-bold text-base z-20 pointer-events-none opacity-0 group-hover:opacity-100">{ result.type }</h2>
<h2 className="absolute w-full top-1/4 text-center font-bold text-base z-20 pointer-events-none opacity-0 group-hover:opacity-100">{ result.title }</h2>
<span id="heart">
<button onClick={ () => FavoriteState(result.mal_id) }> // all the color change when clicked
{favorite ? (
<i className="fa fa-heart absolute top-0 right-0 p-2 font-bold text-base z-20 opacity-0 group-hover:opacity-100" aria-hidden="true" >
</i>
) : (
<i className="fa fa-heart-o absolute top-0 right-0 p-2 font-bold text-base z-20 opacity-0 group-hover:opacity-100" aria-hidden="true" >
</i>
)}
</button>
</span>
</div>
</div>
)) }
</div>
);
}
sorry im new to reactjs and map thing and im lost..., and thanks for the answer

Pass selected component to next step of form react

I have a multi step form, which is a quiz. I already set up the context to be able to pass data.
import { useState, createContext, useContext } from "react";
export const QuizContext = createContext();
export default function QuizProvider({ children }) {
const [data, setData] = useState({});
const setQuizValues = (values) => {
setData((prevValues) => ({
...prevValues,
...values,
}));
};
return (
<QuizContext.Provider value={{ data, setQuizValues }}>
{children}
</QuizContext.Provider>
);
}
export const useQuizData = () => useContext(QuizContext);
So, my first step is selecting a card.
The code below:
import { Card } from "../../stories/Card";
import { useQuizData } from "../../context/index"
import { useState } from "react";
const tacos = [
{
cathegory: 'Meat',
imgURL: 'https://images.unsplash.com/photo-1560781290-7dc94c0f8f4f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3024&q=80'
},
{
cathegory: 'Fish',
imgURL: 'https://images.unsplash.com/photo-1510130387422-82bed34b37e9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=975&q=80'
},
{
cathegory: 'Veggi',
imgURL: 'https://images.unsplash.com/photo-1572527129705-a6c197003d61?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=975&q=80'
},
]
export const TacoCathegories = ({quizStep, prevQuizStep, nextQuizStep}) => {
// const { setQuizValues } = useQuizData();
const [isSelected, setisSelected] = useState();
const handleSubmit = (values) => {
setQuizValues(values);
prevQuizStep();
nextQuizStep();
};
return (
<div className={quizStep === 0 ? 'block': 'hidden'}>
<div className="text-center">
<h2 className="text-3xl font-extrabold tracking-tight text-gray-600 sm:text-4xl">What is your favourite taco group?</h2>
</div>
<div className="max-w-7xl mx-auto py-24 px-4 sm:px-6 lg:px-8">
<div className="mt space-y-12 lg:space-y-0 lg:grid lg:grid-cols-3 lg:gap-x-8">
{tacos.map((taco, index) => (
<Card
role="button"
key={index}
title={taco.cathegory}
source={taco.imgURL}
text={`image of ${taco.cathegory} `}
selected={isSelected === index}
onChange={() => setisSelected(index)}
/>
))}
</div>
{tacos[isSelected] && <p>{tacos[isSelected].cathegory}</p>}
<div className="mt-5 sm:mt-8 sm:flex sm:justify-center lg:justify-center">
<div className="rounded-md shadow">
<a role="button" tabIndex={0}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-gray-200 hover:bg-gray-200 focus:outline-none md:py-4 md:text-lg md:px-10 cursor-not-allowed"
>
Back
</a>
</div>
<div className="mt-3 sm:mt-0 sm:ml-3">
<a
onClick={nextQuizStep}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yellow-500 hover:bg-yallow-600 md:py-4 md:text-lg md:px-10"
>
Next
</a>
</div>
</div>
</div>
</div>
);
}
I am able to get the category of selected card tacos[isSelected].cathegory. Depending on the category I need to render different content in Step 2 of my multistep form. Basically, if I choose Meat I will render cards with Meat Tacos, If I choose Fish - with Fish Tacos. For now second step is empty, because I couldn't figure out how to pass selected category to second step.
export const TacoTypes = ({quizStep, prevQuizStep, nextQuizStep}) => {
return (
<div className={quizStep === 1 ? 'block' : 'hidden'}>
<div>
<p>Taco Types are: all you find in the object</p>
</div>
<div className="mt-5 sm:mt-8 sm:flex sm:justify-center lg:justify-center">
<div className="rounded-md shadow">
<a role="button" tabIndex={0}
onClick={prevQuizStep}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yellow-500 hover:bg-yallow-600 md:py-4 md:text-lg md:px-10"
>
Back
</a>
</div>
<div className="mt-3 sm:mt-0 sm:ml-3">
<a
onClick={nextQuizStep}
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-yellow-500 hover:bg-yallow-600 md:py-4 md:text-lg md:px-10"
>
Next
</a>
</div>
</div>
</div>
)
}
I am new to react, so any tipp would be helpful!
Sandbox: https://codesandbox.io/s/agitated-euler-pny5n

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