简体   繁体   中英

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM