Blog

Autenticazione React con il jwt di laravel 9

Tempo di lettura: 22 minuti

Nella prima parte del tutorial  abbiamo realizzato  una REST API che consentisse l’autenticazione react con il jwt di laravel9.

Abbiamo quindi creato un backend con Laravel 9 e la libreria di terze parti  tymondesigns/jwt-auth che consente un’autenticazione sicura, infine abbiamo testato l’applicazione  con postman.

Ora non ci resta che realizzare il front-end che si connette al suddetto mediante un form di registrazione e autenticazione, a tale scopo utilizzeremo la libreria React.

Autenticazione React con il jwt di laravel 9: crea il progetto React

Aprite una directory a piacere nel vostro pc, quindi da terminale puntate su di essa e digitate:

npx create-react-app@latest movie

attendete che tutte le dipendenze siano scaricate, il tempo varia a seconda dalla velocità di connessione.

Perfetto, aprite il componente App.js e pulitelo come di seguito:

import './App.css';

function App() {
  return (
    <div className="App">
      Hello world!
    </div>
  );
}

export default App;

prima di avvviare il server spostatevi all’interno di movie:

cd movie
npm start
Installa bootstrap

Diamo un tocco di stile al layout, installiamo il framework bootstrap, da terminale digitate:

npm i bootstrap@latest

quindi riaprite il componente App.js ed importate il framework:

import 'bootstrap/dist/css/bootstrap.min.css';

function App() {
  return (
    <div className="container">
      Hello world!
    </div>
  );
}

export default App;

 

react_con_il_jwt_di_laravel_9

Aggiungi la navBar

Collegati su w3schools e scegli il tutorial relativo a bootstrap5:

react_con_il_jwt_di_laravel_9

quindi nella barra a sinistra scegliete navbar:

quindi copiatevi il codice relativo alla prima navbar e convertiamolo in jsx grazie all’aiuto del sito Html to jsx , perfetto ora incollatelo in App.js ,è chiaro che dovremo modificare i link al momento come di seguito:

import 'bootstrap/dist/css/bootstrap.min.css';

function App() {
  return (
    <div>
      <nav className="navbar navbar-expand-sm navbar-dark bg-dark">
        <div className="container-fluid">
          <a className="navbar-brand" href="#">Logo</a>
          <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mynavbar">
            <span className="navbar-toggler-icon" />
          </button>
          <div className="collapse navbar-collapse" id="mynavbar">
            <ul className="navbar-nav me-auto">
              <li className="nav-item">
                <a className="nav-link active" href="#">Home</a>
              </li>
              <li className="nav-item">
                <a className="nav-link" href="#">Login</a>
              </li>
              <li className="nav-item">
                <a className="nav-link" href="#">Dashboard</a>
              </li>
            </ul>
            <form className="d-flex">
              <input className="form-control me-2" type="text" placeholder="Search" />
              <button className="btn btn-primary" type="button">Search</button>
            </form>
          </div>
        </div>
      </nav>
    </div>
  );
}

export default App;

React con il jwt di laravel9: Installa react-router

E’ arrivato il momento di gestire il routing in React, da terminale digitate:

npm i react-router-dom

ora aprite il file index.js ed importate il BrowserRouter e modificate come di seguito:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from 'react-router-dom'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

riaprite il file App.js ed importate Route, Routes e Link dalla libreria di terze parti react-router-dom:

...
import {Route, Routes,Link} from 'react-router-dom'
...

ora possiamo aggiungere il gruppo di rotte da gestire all’interno del div di classe container:

...
     </nav>
      <div className="container">
        <Routes>
          <Route path='/' element={<Home/>}/>
        </Routes>
      </div>
    </div>
  );
}

export default App;
Crea componente Home

Il codice ci esorta a realizzare il componente Home. All’interno della directory src crea la directory components, così da avere una struttura più ordinata:

ora all’interno di components create il file Home.js e modificate come di seguito:

import React from 'react';

const Home = () => {
    return (
        <div>
            
        </div>
    );
};

export default Home;

Nota bene se utilizzate Visual studio code ed avete installato l’estensione per React ReactJS Snippets vi basterà digitare il comando rsc perchè venga realizzato il componente stateless velocemente.

ora importiamolo all’interno di App.js:

import 'bootstrap/dist/css/bootstrap.min.css';
import {Route, Routes,Link} from 'react-router-dom'
import Home from './components/Home';
...
Crea componenti Login e Dashboard

Realiziamo gli altri componenti , cioè Login e Dashboard sempre all’interno della folder components:

Login.js

import React from 'react';

const Login = () => {
    return (
        <div>
            
        </div>
    );
};

export default Login;

Dashboard.js

import React from 'react';

const Dashboard = () => {
    return (
        <div>
            
        </div>
    );
};

export default Dashboard;

importate anche questi in App.js:

...
import Dashboard from './components/Dashboard';
import Home from './components/Home';
import Login from './components/Login';
...

possiamo ora aggiungere le path alla Route:

...
      <div className="container">
        <Routes>
          <Route path='/' element={<Home/>}/>
          <Route path='/login' element={<Login />} />
          <Route path='/dashboard' element={<Dashboard />} />
        </Routes>
      </div>
...

quindi cambia il tag a in Link e l’attributo href in to come di seguito:

...
              <li className="nav-item">
                <Link className="nav-link active" to="/">Home</Link>
              </li>
              <li className="nav-item">
                <Link className="nav-link" to="/login">Login</Link>
              </li>
              <li className="nav-item">
                <Link className="nav-link" to="/dashboard">Dashboard</Link>
              </li>
...

Inserite un testo nei vari componenti:

import React from 'react';

const Home = () => {
    return (
        <div>
            <h1>HELLO HOME</h1>
        </div>
    );
};

export default Home;

fatelo anche su Login e Dashboard, giusto per distinguerli. Se navigate tra le viste noterete il cambio di rotta.

Aggiungi il form su Login.js

Navigate su w3schools.com/bootstrap5 scegliendo la parte relativa al form. Copiatevi il codice relativo al primo esempio e, come in precedenza, convertitelo in jxs, quindi aggiungetelo in Login.js:

import React from 'react';

const Login = () => {
    return (
        <div className='row justify-content-center pt-5'>
            <div className="mb-3 mt-3">
                <label htmlFor="email" className="form-label">Email:</label>
                <input type="email" className="form-control" id="email" placeholder="Enter email" name="email" />
            </div>
            <div className="mb-3">
                <label htmlFor="pwd" className="form-label">Password:</label>
                <input type="password" className="form-control" id="pwd" placeholder="Enter password" name="pswd" />
            </div>
            <button type="button" className="btn btn-primary">Login</button>
        </div>
    );
};

export default Login;
Gestire lo stato negli hooks

Aprite il componente Login.js e modificate come diseguito:

import React, { useState } from 'react';

const Login = () => {
    const [email,setEmail]=useState();
    const [password,setPassword]=useState();
    return (
...

verificate che venga importato useState da react.

Ora modificate i campi input ed il button , aggiungendo gli eventi:

import React, { useState } from 'react';

const Login = () => {
    const [email,setEmail]=useState();
    const [password, setPassword] = useState();

    const submitForm =() => {
        console.log(email + ' '+ password)
    }
    return (
        <div className='row justify-content-center pt-5'>
            <div className="mb-3 mt-3">
                <label htmlFor="email" className="form-label">Email:</label>
                <input onChange={e => setEmail(e.target.value)} type="email" className="form-control" id="email" placeholder="Enter email" name="email" />
            </div>
            <div className="mb-3">
                <label htmlFor="pwd" className="form-label">Password:</label>
                <input onChange={e => setPassword(e.target.value)} type="password" className="form-control" id="pwd" placeholder="Enter password" name="password" />
            </div>
            <button type="button" onClick={submitForm} className="btn btn-primary">Login</button>
        </div>
    );
};

export default Login;

testiamo l’applicazione , aprite http://localhost:3000/login ed inserite una email ed una password nei relativi campi:

ok tutto funziona!

Installa la libreria di terze parti Axios

Per gestire le chiamate all’API scaricheremo la libreria di terze parti Axios, da terminale digitate:

npm i axios

sotto la directory source createvi la cartella services:

all’interno della stessa createvi il file AuthUser.js. Apritelo e modifcate come segue:

import axios from 'axios';

export default function AuthUser() {
    const http = axios.create({
        baseURL: "http://127.0.0.1:8000/api/auth",
        headers:{
            "Content-type" : "application/json"
        }
    });
    return http;
}

ora importate il service in Login.js e definiamo la chiamata nel metodo submitForm:

import React, { useState } from 'react';
import AuthUser from '../services/AuthUser';

const Login = () => {
    const { http } = AuthUser();
    const [email, setEmail] = useState();
    const [password, setPassword] = useState();

    const submitForm = () => {
        //console.log(email + ' '+ password)
        http.post('/login', { email: email, password: password })
            .then((res)=>{
                console.log(res.data)
            })

    }
    return (
        <div className='row justify-content-center pt-5'>
            <div className="mb-3 mt-3">
                <label htmlFor="email" className="form-label">Email:</label>
                <input onChange={e => setEmail(e.target.value)} type="email" className="form-control" id="email" placeholder="Enter email" name="email" />
            </div>
            <div className="mb-3">
                <label htmlFor="pwd" className="form-label">Password:</label>
                <input onChange={e => setPassword(e.target.value)} type="password" className="form-control" id="pwd" placeholder="Enter password" name="password" />
            </div>
            <button type="button" onClick={submitForm} className="btn btn-primary">Login</button>
        </div>
    );
};

export default Login;

se testate l’app noterete in console un  errore come questo:

 

questo perchè in effetti l’utente non esiste, per cui aprite il vostro gestore dei db, nel mio caso phpmyadmin ed inserite un utente ,al momento, manualmente. Se hai seguito lo scorso tutorial nel form digitate quello creato in fase di test con Postman:

ok, provate ora:

tutto funziona correttamente.

Salva il token nel localStorage

Nel serice AuthUser.js andremo a far sì che il token e lo User restituiti in fase di autenticazione vengano poi salvati in locale grazie al localStorage, apritelo e modificate come di seguito:

import { useState } from 'react';
import axios from 'axios';

export default function AuthUser() {
    const [token, setToken] = useState();
    const [user, setUser] = useState();

    const saveToken = (user, token) => {
        sessionStorage.setItem('token', JSON.stringify(token));
        sessionStorage.setItem('user', JSON.stringify(user));

        setToken(token);
        setUser(user);
    }
    const http = axios.create({
        baseURL: "http://127.0.0.1:8000/api/auth",
        headers: {
            "Content-type": "application/json"
        }
    });
    return {
        http
    }
}
Gestisci il redirect con la libreria di React UseNavigate

Una volta settati il token e lo User nel sessionStorage in locale non ci resta che gestire il redirect dello stesso grazie alla libreria useNavigate di React, allora importiamola subito:

import { useState } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';

export default function AuthUser() {
    const navigate = useNavigate();

    const getToken =() => {
        const tokenString = sessionStorage.getItem('token');
        const userToken = JSON.parse(tokenString);
        return userToken;
    }

    const getUser = () => {
        const userString = sessionStorage.getItem('user');
        const user_detail = JSON.parse(userString);
        return user_detail;
    }
    const saveToken = (user, token) => {
        sessionStorage.setItem('token', JSON.stringify(token));
        sessionStorage.setItem('user', JSON.stringify(user));

        setToken(token);
        setUser(user);
        navigate('/dashboard');
    }
    const [token, setToken] = useState(getToken());
    const [user, setUser] = useState(getUser());
    
    const http = axios.create({
        baseURL: "http://127.0.0.1:8000/api/auth",
        headers: {
            "Content-type": "application/json"
        }
    });
    return {
        setToken:saveToken,
        token,
        user,
        getToken,
        http
    }
}

 

inoltre abbiamo realizzato i metodi che mi consentiranno di leggere i dati relativi al token ed allo user, alla fine li ritorniamo.

Aprite il file Login.jse modificate come di seguito:

...
const Login = () => {
    const { http, setToken } = AuthUser();
    const [email, setEmail] = useState();
    const [password, setPassword] = useState();

    const submitForm = () => {
        //console.log(email + ' '+ password)
        http.post('/login', { email: email, password: password })
            .then((res)=>{
                setToken(res.data.user, res.data.access_token)
            })

    }
...

una volta fatto proviamo a loggarci,se tutto è a posto verremo reindirizzati nella dashboard e:

 

il token e lo user sono stati salvati nel sessionStorage in JSON.

Crea il componente Navbar

Organizziamo il codice  in maniera più ordinata, inseriamo la navbar in un suo componente. Createvi la directory shared all’interno della directory src ed al suo interno create il componente Navbar.js:

aprite il file Navbar.js ed incollate il codice relativo alla navbar copiat da App.js, ricordatevi di importare Link da react-router-dom:

import React from 'react';
import { Link } from 'react-router-dom';

const Navbar = () => {
    return (
        <div>
            <nav className="navbar navbar-expand-sm navbar-dark bg-dark">
                <div className="container-fluid">
                    <Link className="navbar-brand" to="/">Logo</Link>
                    <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mynavbar">
                        <span className="navbar-toggler-icon" />
                    </button>
                    <div className="collapse navbar-collapse" id="mynavbar">
                        <ul className="navbar-nav me-auto">
                            <li className="nav-item">
                                <Link className="nav-link active" to="/">Home</Link>
                            </li>
                            <li className="nav-item">
                                <Link className="nav-link" to="/login">Login</Link>
                            </li>
                            <li className="nav-item">
                                <Link className="nav-link" to="/dashboard">Dashboard</Link>
                            </li>
                        </ul>
                        <form className="d-flex">
                            <input className="form-control me-2" type="text" placeholder="Search" />
                            <button className="btn btn-primary" type="button">Search</button>
                        </form>
                    </div>
                </div>
            </nav>   
        </div>
    );
};

export default Navbar;

ora aprite App.js ed importate il componente Navbar.js:

import 'bootstrap/dist/css/bootstrap.min.css';
import {Route, Routes} from 'react-router-dom'
import Dashboard from './components/Dashboard';
import Home from './components/Home';
import Login from './components/Login';
import Navbar from './shared/Navbar';


function App() {
  return (
    <div>
      <Navbar/>
      <div className="container">
        <Routes>
          <Route path='/' element={<Home/>}/>
          <Route path='/login' element={<Login />} />
          <Route path='/dashboard' element={<Dashboard />} />
        </Routes>
      </div>
    </div>
  );
}

export default App;

bene tutto funziona come prima.

Cambia la Navbar in caso di autenticazione o meno

Nel caso in cui siamo loggati non ha senso mostrare nella navbar l’indirizzo al login o viceversa se non lo siamo la dashboard. Quindi faremo in modo che, una volta autenticati, dal menu non sarà visibile login ma mostreremo logout e dashboard.

All’interno della directory shared createvi due files Guest.js ed Auth.js. Aprite il primo e modificate come di seguito:

import React from 'react';
import { Link, Route, Routes } from 'react-router-dom';
import Login from '../components/Login';
import Register from '../components/Register';
import Home from './../components/Home';
const Guest = () => {
    return (
        <div>
            <nav className="navbar navbar-expand-sm navbar-dark bg-dark">
                <div className="container-fluid">
                    <Link className="navbar-brand" to="/">Logo</Link>
                    <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mynavbar">
                        <span className="navbar-toggler-icon" />
                    </button>
                    <div className="collapse navbar-collapse" id="mynavbar">
                        <ul className="navbar-nav me-auto">
                            <li className="nav-item">
                                <Link className="nav-link active" to="/">Home</Link>
                            </li>
                            <li className="nav-item">
                                <Link className="nav-link" to="/login">Login</Link>
                            </li>
                            <li className="nav-item">
                                <Link className="nav-link" to="/register">Register</Link>
                            </li>
                        </ul>
                        <form className="d-flex">
                            <input className="form-control me-2" type="text" placeholder="Search" />
                            <button className="btn btn-primary" type="button">Search</button>
                        </form>
                    </div>
                </div>
            </nav>
            <div className="container">
                <Routes>
                    <Route path='/' element={<Home />} />
                    <Route path='/login' element={<Login />} />
                    <Route path='/dashboard' element={<Register />} />
                </Routes>
            </div>
        </div>
    );
};

export default Guest;

in pratica ho copiato il contenuto di navBar ed incollato qui, modificando le voci relative ai Link che devono essere quelle in caso di utente ospite quindi non loggato.
Potete cancellare il componente navBar, non ci serve più.

Modificate il componente Auth.js così:

import React from 'react';
import { Link, Route, Routes } from 'react-router-dom';
import Home from '../components/Home';
import Dashboard from './../components/Dashboard';
const Auth = () => {
    return (
        <div>
            <nav className="navbar navbar-expand-sm navbar-dark bg-dark">
                <div className="container-fluid">
                    <Link className="navbar-brand" to="/">Logo</Link>
                    <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mynavbar">
                        <span className="navbar-toggler-icon" />
                    </button>
                    <div className="collapse navbar-collapse" id="mynavbar">
                        <ul className="navbar-nav me-auto">
                            <li className="nav-item">
                                <Link className="nav-link active" to="/">Home</Link>
                            </li>
                            <li className="nav-item">
                                <Link className="nav-link" to="/dashboard">Dashboard</Link>
                            </li>
                            <li className="nav-item">
                                <Link className="nav-link" to="/login">Logout</Link>
                            </li>
                        </ul>
                        <form className="d-flex">
                            <input className="form-control me-2" type="text" placeholder="Search" />
                            <button className="btn btn-primary" type="button">Search</button>
                        </form>
                    </div>
                </div>
            </nav>
            <div className="container">
                <Routes>
                    <Route path='/' element={<Home />} />
                    <Route path='/dashboard' element={<Dashboard />} />
                </Routes>
            </div>
        </div>
    );
};

export default Auth;

Con Link relativi a voci in caso di autenticazione.

Ora puliamo il componente App.js ed importiamo i  componenti realizzati poco sopra:

import 'bootstrap/dist/css/bootstrap.min.css';
import AuthUser from './services/AuthUser';
import Auth from './shared/Auth';
import Guest from './shared/Guest';
function App() {
  const { getToken } = AuthUser();
  if (!getToken()) {
    return (
      <Guest />
    );
  }
  return (
    <Auth />
  );
}

export default App;

 

come potete notare importo anche il metodo getToken dal service AuthUser tramite il quale posso stabilire se esiste o meno un token valido relativo ad un utente e quindi mostrare il menù Guest o Auth, semplice vero 🙂 🙂

Implementa il logout

Innanzitutto modifichiamo il menu in Auth.js, in quanto non dobbiamo creare nessun componente relativo al logout ma ci basta un metodo che cancelli il token:

...
                            <li className="nav-item">
                                <Link className="nav-link" to="/dashboard">Dashboard</Link>
                            </li>
                            <li className="nav-item">
                                <span role='button' className="nav-link" onClick={logoutUser}>Logout</span>
                            </li>
...

quindi implementiamo il metodo logoutUser:

...
const Auth = () => {
    const {token, logout} = AuthUser();
    const logoutUser =() => {
        if(token !== undefined){
            logout();
        }
    }
...

come si evince importo i metodi token e logout da authUser. Il secondo dobbiamo ancora crearlo, per cui apriamo AuthUser e realizziamolo:

...
    const saveToken = (user, token) => {
        sessionStorage.setItem('token', JSON.stringify(token));
        sessionStorage.setItem('user', JSON.stringify(user));

        setToken(token);
        setUser(user);
        navigate('/dashboard');
    }

    const logout =() => {
        sessionStorage.clear();
        navigate('/login');
    }
...
return {
        setToken:saveToken,
        token,
        user,
        getToken,
        logout,
        http
    }

non dimenticatevi anche di esportarlo.

Implementiamo la Dashboard

Aprite il componente Dashboard e modificate come di seguito:

import React from 'react';
import AuthUser from '../services/AuthUser';

const Dashboard = () => {
    const {user} = AuthUser();
    return (
        <div>
            HELLO <small><b>{user.name}</b> your email is <b>{user.email}</b></small>
        </div>
    );
};

export default Dashboard;

semplicemente importiamo il metodo user da AuthUser il quale contiene le info relative all’utente loggato e le mostriamo a video grazie all’interpolazione.

Implementiamo il componente Register

Se non lo avete fatto create il componente Register.js all’interno della directory components e modificate di conseguenza, in pratica per iniziare potete copiare ed incollare il codice di Login.js, poi apporteremo le dovute modifiche:

import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import AuthUser from '../services/AuthUser';

const Register = () => {
    const navigate = useNavigate();
    const { http } = AuthUser();
    const [email, setEmail] = useState();
    const [name, setName] = useState();
    const [password, setPassword] = useState();
    const [passwordConf, setPasswordConf] = useState();

    const submitForm = () => {
        http.post('/register', { name: name, email: email, password: password, password_confirmation: passwordConf })
            .then((res) => {
                navigate('/login')
            })

    }
    return (
        <div className='row justify-content-center pt-5'>
            <div className="mb-3 mt-3">
                <label htmlFor="name" className="form-label">Name:</label>
                <input onChange={e => setName(e.target.value)} type="email" className="form-control" id="name" placeholder="Enter name" name="name" />
            </div>
            <div className="mb-3 mt-3">
                <label htmlFor="email" className="form-label">Email:</label>
                <input onChange={e => setEmail(e.target.value)} type="email" className="form-control" id="email" placeholder="Enter email" name="email" />
            </div>
            <div className="mb-3">
                <label htmlFor="password" className="form-label">Password:</label>
                <input onChange={e => setPassword(e.target.value)} type="password" className="form-control" id="password" placeholder="Enter password" name="password" />
            </div>
            <div className="mb-3">
                <label htmlFor="password_confirmation" className="form-label">Password confirmation:</label>
                <input onChange={e => setPasswordConf(e.target.value)} type="password" className="form-control" id="password_confirmation" placeholder="Enter password confirmation" name="password_confirmation" />
            </div>
            <button type="button" onClick={submitForm} className="btn btn-primary">Register</button>
        </div>
    );
};

export default Register;

innanzitutto importiamo useNavigate da react-router-dom il quale mi consentirà una redirect alla pagina del login.

Quindi definisco gli stati , al momento vuoti, di name e password_confirmation essenziali ai fini della registrazione, per cui aggiungo anche i relativi campi.

Navigate all’indirizzo http://localhost:3000/register ,provate a registrarvi, se verrete reindirizzati su Login è tutto ok! 🙂 🙂

Validazione dei form con Yup e react-hook-form

Ora che siamo riusciti anche ad implementare la registrazione è obbligatorio gestire la validazione relativamente ai campi dello stesso. Per far ciò ci serviremo delle librerie di terze parti Yup e react-hook-form,  da terminale digitate:

npm install react-hook-form @hookform/resolvers yup

bene quindi importate useForm:

import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import AuthUser from '../services/AuthUser';
import { useForm } from "react-hook-form";
//..

ora possiamo utilizzare i metodi di useForm, definite la costante:

...
import { useForm } from "react-hook-form";
const Register = () => {
    const { register, handleSubmit, formState: { errors }, reset } = useForm();
...

dove:

  • register() consente di registrare un elemento e di applicare le regole di validazione appropriate;
  • handleSubmit() riceve i dati del form se la convalida dei campi è andata a buon fine;
  • reset() cancella i dati del form e ripristina i valori iniziali;
  • formState() restituisce eventuali errori del form in maniera semplice

bene quindi importiamo Yup nel progetto e creiamo lo schema:

...
import { useForm } from "react-hook-form";
import * as Yup from 'yup'
const Register = () => {
    const formSchema = Yup.object().shape({
        name: Yup.string()
            .required('Name is mendatary')
            .min(3, 'Name must be at 3 char long'),
        email: Yup.string().email('Must be a valid email').max(255)
            .required('Email is required'),
        password: Yup.string()
            .required('Password is mendatory')
            .min(8, 'Password must be at 3 char long'),
        password_confirmation: Yup.string()
            .required('Password is mendatory')
            .oneOf([Yup.ref('password')], 'Passwords does not match'),
    })
    const { register, handleSubmit, formState: { errors }, reset } = useForm();
...

per poter utilizzare la costante formSchema è necessario importare anche @hookform/resolvers/yup:

...
import { useForm } from 'react-hook-form'
import { yupResolver } from '@hookform/resolvers/yup'
...

quindi modificate come segue:

...
    const { register, handleSubmit, formState: { errors }, reset } = useForm({
        resolver:yupResolver(formSchema)
    });
...
Realizza handleSubmit() metodo che invia i dati

Aggiungete il metodo handleSubmit() il quale invia i dati e poi fa il reset:

...    
const submitForm = (data) => {
        console.log(JSON.stringify(data, null, 4))
        http.post('/register', { name: name, email: email, password: password, password_confirmation: passwordConf })
            .then((res) => {
                navigate('/login')
            })
        reset();

 }
...

 

quindi aggiungete il form il quale avvolgerà i campi realizzati in precedenza:

...
    return (
        <div className='row justify-content-center pt-5'>
            <form onSubmit={handleSubmit(submitForm)}>
                <div className="mb-3 mt-3">
                    <label htmlFor="name" className="form-label">Name:</label>
                    <input onChange={e => setName(e.target.value)} type="text" className="form-control" id="name" placeholder="Enter name" name="name" />
        </div>
...

ora modificate i campi di input inmodo tale che vengano registrati secondo le proprietà di

...
                  <form onSubmit={handleSubmit(submitForm)}>
                        <div className="form-group">
                            <label>Name</label>
                            <input
                                name="name"
                                type="pastextsword"
                                {...register('name')}
                                className={`form-control ${errors.name ? 'is-invalid' : ''}`}
                                onChange={onChangeName}
                            />
                            <div className="invalid-feedback">{errors.name?.message}</div>
                        </div>
                        <div className="form-group">
                            <label>Email</label>
                            <input
                                name="email"
                                type="text"
                                {...register('email')}
                                onChange={onChangeEmail}
                                className={`form-control ${errors.email ? 'is-invalid' : ''}`}
                            />
                            <div className="invalid-feedback">{errors.email?.message}</div>
                        </div>
                        <div className="form-group">
                            <label>Password</label>
                            <input
                                name="password"
                                type="password"
                                {...register('password')}
                                onChange={onChangePassword}
                                className={`form-control ${errors.password ? 'is-invalid' : ''}`}
                            />
                            <div className="invalid-feedback">{errors.password?.message}</div>
                        </div>
                        <div className="form-group">
                            <label>Confirm Password</label>
                            <input
                                name="password_confirmation"
                                type="password"
                                {...register('password_confirmation')}
                                onChange={onChangePasswordConf}
                                className={`form-control ${errors.password_confirmation ? 'is-invalid' : ''}`}
                            />
                            <div className="invalid-feedback">{errors.password_confirmation?.message}</div>
                        </div>
                        <div className="form-group">
                            <button type="submit" className="btn btn-warning btn-block mt-4" disabled={loading}>
                                {loading && (
                                    <span className='spinner-border spinner-border-sm'></span>
                                )}
                                <span>Register</span>
                            </button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
...

quindi aggiungiamo gli eventi onChange, ecco il codice completo di Register.js:

import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import AuthUser from '../services/AuthUser';
import { useForm } from "react-hook-form";
import * as Yup from 'yup'
import { yupResolver } from '@hookform/resolvers/yup';
const Register = () => {
    const formSchema = Yup.object().shape({
        name: Yup.string()
            .required('Name is mendatary')
            .min(3, 'Name must be at 3 char long'),
        email: Yup.string().email('Must be a valid email').max(255)
            .required('Email is required'),
        password: Yup.string()
            .required('Password is mendatory')
            .min(8, 'Password must be at 3 char long'),
        password_confirmation: Yup.string()
            .required('Password is mendatory')
            .oneOf([Yup.ref('password')], 'Passwords does not match'),
    })
    const { register, handleSubmit, formState: { errors }, reset } = useForm({
        resolver: yupResolver(formSchema)
    });
    const navigate = useNavigate();
    const { http } = AuthUser();
    const [email, setEmail] = useState();
    const [name, setName] = useState();
    const [password, setPassword] = useState();
    const [passwordConf, setPasswordConf] = useState();
    const [loading, setLoading] = useState(false);
    const onChangeEmail = (e) => {
        const email = e.target.value;
        setEmail(email);
    }

    const onChangeName = (e) => {
        const name = e.target.value;
        setName(name);
    }
    const onChangePassword = (e) => {
        const password = e.target.value;
        setPassword(password);
    }
    const onChangePasswordConf = (e) => {
        const passwordConf = e.target.value;
        setPasswordConf(passwordConf);
    }

    const submitForm = (data) => {
        setLoading(true);
        console.log(JSON.stringify(data, null, 4))
        http.post('/register', { name: name, email: email, password: password, password_confirmation: passwordConf })
            .then((res) => {
                navigate('/login')
            })
        reset();

    }
    return (
        <div className='row justify-content-center pt-5'>
            <div className="col-sm-6">
                <div className="card p-4">
                    <img
                        src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
                        alt="profile-img"
                        className="profile-img-card"
                    />
                    <h2>Register</h2>
                    <form onSubmit={handleSubmit(submitForm)}>
                        <div className="form-group">
                            <label>Name</label>
                            <input
                                name="name"
                                type="pastextsword"
                                {...register('name')}
                                className={`form-control ${errors.name ? 'is-invalid' : ''}`}
                                onChange={onChangeName}
                            />
                            <div className="invalid-feedback">{errors.name?.message}</div>
                        </div>
                        <div className="form-group">
                            <label>Email</label>
                            <input
                                name="email"
                                type="text"
                                {...register('email')}
                                onChange={onChangeEmail}
                                className={`form-control ${errors.email ? 'is-invalid' : ''}`}
                            />
                            <div className="invalid-feedback">{errors.email?.message}</div>
                        </div>
                        <div className="form-group">
                            <label>Password</label>
                            <input
                                name="password"
                                type="password"
                                {...register('password')}
                                onChange={onChangePassword}
                                className={`form-control ${errors.password ? 'is-invalid' : ''}`}
                            />
                            <div className="invalid-feedback">{errors.password?.message}</div>
                        </div>
                        <div className="form-group">
                            <label>Confirm Password</label>
                            <input
                                name="password_confirmation"
                                type="password"
                                {...register('password_confirmation')}
                                onChange={onChangePasswordConf}
                                className={`form-control ${errors.password_confirmation ? 'is-invalid' : ''}`}
                            />
                            <div className="invalid-feedback">{errors.password_confirmation?.message}</div>
                        </div>
                        <div className="form-group">
                            <button type="submit" className="btn btn-warning btn-block mt-4" disabled={loading}>
                                {loading && (
                                    <span className='spinner-border spinner-border-sm'></span>
                                )}
                                <span>Register</span>
                            </button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    );
};

export default Register;

come potete notare nel codice ho inserito anche un semplicissimo loader abbinato al pulsante di invio. Cosa che possiamo anche aggiungere al form nel login:

import React, { useState } from 'react';
import AuthUser from '../services/AuthUser';

const Login = () => {
    const { http, setToken } = AuthUser();
    const [email, setEmail] = useState();
    const [password, setPassword] = useState();
    const [loading, setLoading] = useState(false);
    const submitForm = () => {
        setLoading(true);
        http.post('/login', { email: email, password: password })
            .then((res)=>{
                setToken(res.data.user, res.data.access_token)
            })

    }
    return (
        <div className='row justify-content-center pt-5'>
            <div className="mb-3 mt-3">
                <label htmlFor="email" className="form-label">Email:</label>
                <input onChange={e => setEmail(e.target.value)} type="email" className="form-control" id="email" placeholder="Enter email" name="email" />
            </div>
            <div className="mb-3">
                <label htmlFor="pwd" className="form-label">Password:</label>
                <input onChange={e => setPassword(e.target.value)} type="password" className="form-control" id="pwd" placeholder="Enter password" name="password" />
            </div>
            <button type="button" onClick={submitForm} className="btn btn-primary">
                {loading && (
                    <span className='spinner-border spinner-border-sm'></span>
                )}
                <span>Login</span>
            </button>
        </div>
    );
};

export default Login;

aggiungiamo un pò di stile nel file App.css:

label {
    display: block;
    margin-top: 10px;
}

.card-container.card {
    max-width: 350px !important;
    padding: 40px 40px;
}

.card {
    background-color: #f7f7f7;
    padding: 20px 25px 30px;
    margin: 0 auto 25px;
    margin-top: 50px;
    -moz-border-radius: 2px;
    -webkit-border-radius: 2px;
    border-radius: 2px;
    -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
    -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
    box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
}

.profile-img-card {
    width: 96px;
    height: 96px;
    margin: 0 auto 10px;
    display: block;
    -moz-border-radius: 50%;
    -webkit-border-radius: 50%;
    border-radius: 50%;
}

.content {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
    display: table;
    text-align: center;
    font-weight: 100;
    display: inline-block;
}

.title {
    color: #7a858a;
    font-size: 96px;
    margin-bottom: 40px;
    font-family: 'Lato';
}

.quote {
    font-size: 24px;
}

Conclusione

In questa guida siamo riusciti a realizzare grazie a React un sistema di autenticazione implementando il progetto all’API realizzata in questo tutorial. Abbiamo gestito anche le varie fasi di autenticazione ed il salvataggio del token restituito dall’API nel sessionStorage in locale.

Come avrete di certo notato abbiamo chiamato la nostra App movie in quanto nel prossimo tutorial gestiremo una chiamata all’EndPoint di OMDB per farci restituire una lista di films in base ad una ricerca relativamente al titolo dello stesso.

Solo coloro che saranno autenticati avranno accesso alla barra di ricerca. Si avrà, inoltre, la possibilità di salvare determinati titoli, a nostra scelta, nei preferiti così come avviene in Netflix o in Prime Video per intendersi.

Puoi scaricare e/o visionare  il codice su GitHub.

Se avrete voglia di seguire gli altri tutorial ne sarò felice 🙂 🙂 🙂

Se volete Approfondire le conoscenze su REACT E NODEJS CON EXPRESS E MONGODB potete seguire il  corso completo su Udemy sempre scontato a 9.99/12.99 € , inoltre, nel momento in cui non foste soddisfatti, avete la possibilità di ottenere il rimborso completo dello stesso entro 30 giorni dalla data di acquisto!

REACT NODEJS FULLSTACK

Se volete Approfondire le conoscenze su REACT e REDUX potete seguire il  corso completo su Udemy sempre scontato a 9.99/12.99 € , inoltre, nel momento in cui non foste soddisfatti, avete la possibilità di ottenere il rimborso completo dello stesso entro 30 giorni dalla data di acquisto!

Lucio Ticali

Chi è ?

Lucio Ticali è un Web & App developer con la passione per il web marketing,si occupa anche di tecniche di indicizzazione del sito e di promozione dello stesso (SEO e SEM).

Lascia la tua opinione