Tempo di lettura: 8 minuti
Ho deciso di scrivere una guida a React Hooks per principianti in quanto sono convinto che questi siano molto più intuitivi e facili da utilizzare rispetto all’utilizzo delle classi, e vedremo come.
Guida a React Hooks per principianti: definizione
Il concetto di Hooks in React venne introdotto nel 2018 ed aggiunto alla libreria dalla versione 16.8. Questi forniscono un miglior approccio per la condivisione e la logica stateful rendendo gli oggetti di scena di rendering(componentDidMount, componentDidUpdate e componentWillUnmount) quasi obsoleti.
In React ci sono una serie di Hooks predefiniti. Tra di essi i più importanti sono useState ed useEffect. Il primo consente l’utilizzo dello stato locale all’interno dei componenti della libreria senza dover ricorrere alle classi ES7.
useEffect sostituisce in pratica componentDidMount, componentDidUpdate e componentWillUnMount con un unica API.
Simile ad useState è userReducer, altro Hook che si occupa di gestire il cambi di stato più complessi.
I componenti di React possono quindi così esser suddivisi:
- componenti funzionali (functional components)
- componenti di classe (class components)
- componenti funzionali con ganci (functional components with hooks)
Per approfondimenti vi rimando a:
Guida a React Hooks per principianti: requisiti
E’ chiaro che per poter seguire la guida devi avere una conoscenza base di React ed ES6, in quanto non mi soffermerò su funzioni freccia, destrutturazione, classi, ecc…
In caso contrario puoi seguire i miei corsi su Udemy:
Imposta il progetto
Crea l’applicazione per poter seguire gli esempi:
npx create-react-app hooks-test
Una struttura più chiara
React come ben sapete è una libreria js atta alla realizzazione di User Interface, uno dei vantaggi della stessa è l’imporre allo sviluppatore un flusso di dati rigoroso.
Questa applica una struttura chiara (ci sono contenitore e componenti di presentazione) e , come dicevo in precedenza, un flusso di dati rigoroso (i componenti reagiscono allo stato e gli oggetti di scena cambiano). Tutto ciò rende lo sviluppo front end molto più semplice rispetto a prima.
Secondo la teoria di base di React una parte della User Interface può reagire in risposta ai cambiamenti di stato. Fino all’avvento degli Hooks la forma base per esprimere questo flusso veniva gestito dalle Classi ES6.
Per comprendere meglio facciamo un esempio pratico, create un component e chiamatelo Button.js:
import React, {Component} from 'react'; class Button extends Component { constructor() { super(); this.state = {count: 1} this.handleClick = this.handleClick.bind(this) } handleClick() { this.setState(()=>{ return {count: this.state.count+1} }) } render() { const {count} = this.state; return ( <div> <button onClick={this.handleClick} className="btn btn-primary">Clicca per aumentare</button> <i style={{marginLeft:"10px"}}>{count}</i> </div> ); } } export default Button;
Come potete notare dal codice, lo stato interno del componente viene modificato (this.setState) ogni qual volta si fa click sul pulsante. Lo stato di Count, inizialmente pari a 1, incrementa di 1 ad ogni click.
Aggiornare lo stato in React con gli Hooks
Ok proviamo ora ad impostare il codice precedente senza la classe , quindi con un componente funzionale. Come faremo a gestirne lo stato senza usare this.setState?
Crea un nuovo componente innanzitutto e chiamalo ButtonHook.js ed inserisci:
import React from 'react'; const ButtonHook = () => { return ( <div> </div> ); }; export default ButtonHook;
Se utilizzi visual studio code puoi installare il plugin React snippets, quindi dopo aver creato il file .js digita per i componenti funzionali lo snippet rsc e digita tab da tastiera, questo lo creerà per voi. In caso di componenti a classi lo snippet è rcc.
Quindi importa l’Hook (gancio) useState:
import React,{useState} from 'react';
Una volta importato useState destruttura due valori:
const [count, setCount] = useState(1)
questa è la destrutturazione dell’array in ES6 o 2015.
Per le variabili puoi utilizzare qualsiasi nomi ma vi consiglio di utilizzare quelli descrittivi e significativi quindi semantici con quello che fai.
L’argomento passato a useState è lo stato iniziale effettivo, dati che saranno soggetti a modifiche. useState restituisce due associazioni:
- valore iniziale dello stato
- funzione di aggiornamento dello stesso
L’esempio precedente con gli Hooks ora diventa:
import React,{useState} from 'react'; const ButtonHook = () => { const [count, setCount] = useState(1) return ( <div> <button onClick={() => setCount(count + 1)}>Clicca per aumentare</button> {count} </div> ); }; export default ButtonHook;
Importate il componente in App.js:
import logo from './logo.svg'; import './App.css'; import ButtonHooks from './components/Button'; ...
file App.js completo:
import logo from './logo.svg'; import './App.css'; import ButtonHooks from './components/Button'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo"/> <ButtonHooks/> </header> </div> ); } export default App;
quindi avviate il server con npm start e collegatevi su http://localhost:3000/ , noterete che il risultato non cambia, ma avrete un codice più semplice e pulito.
Guida a React Hooks per principianti: recupero dati API
In un componente a classe il recupero dei dati da un’API viene recuperato tramite il componentDidMount , create un componente e chiamatelo Dati.js , aggiungete:
import React, {Component} from 'react'; class Dati extends Component { state = {data: []} componentDidMount() { fetch("https://jsonplaceholder.typicode.com/users") .then(res => res.json()) .then(data => this.setState(() => { return {data} }) ); } render() { return ( <div> <ul className={""}> {this.state.data.map(el => ( <li className="list-group" key={el.id}>{el.name} - {el.email}</li> ))} </ul> </div> ); } } export default Dati;
quindi importatelo in App.js e provatelo:
import logo from './logo.svg'; import './App.css'; import ButtonHooks from './components/Button'; import Dati from './components/Dati'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo"/> <ButtonHooks/> <Dati/> </header> </div> ); } export default App;
avviate il server e provatelo.
Tuttavia il suddetto codice non è riutilizzabile, per condividere i dati con eventuali componenti figli dovremmo utilizzare un rendering prop:
import React, {Component} from 'react'; class Dati extends Component { state = {data: []} componentDidMount() { fetch("https://jsonplaceholder.typicode.com/users") .then(res => res.json()) .then(data => this.setState(() => { return {data} }) ); } render() { return this.props.render(this.state.data); } } export default Dati;
ora nel componente figlio andresti a scrivere:
<Dati render={data => { return( <div> <ul> {data.map(el=>( <li key={el.id}>{el.name} - {el.email}</li> ))} </ul> </div> ) }} />
Un pò troppo macchinoso per una cosa così semplice non trovate? Ecco perchè nascono gli hooks 🙂 🙂
Recupero dati con useEffect
useEffect ,in pratica, sostituisce componentDidMount, componentDidUpdate e componentWillUnmount dei componenti a classi unificando il tutto in unica soluzione.
Andiamo a riscrivere il component Dati come componente funzionale con ganci. Vedremo che al posto di chiamare this.setState possiamo utilizzare setData, ossia una funzione arbitraria estratta da useState:
import React, {useState, useEffect} from 'react'; const DatiHooks = () => { const [data, setData] = useState([]); useEffect(() => { fetch("https://jsonplaceholder.typicode.com/users") .then(res => res.json()) .then(data => setData(data)); }) return ( <div> <ul> {data.map(el =>( <li key={el.id}>{el.name} - {el.email}</li> ))} </ul> </div> ); }; export default DatiHooks;
importiamolo ed utiliziamolo in App.js:
... import DatiHooks from './components/DatiHooks'; ...
se avviate il server sembra funzionare perfettamente, tuttavia se avviate la console:
come da immagine noterete che users viene chiamato diverse volte, avvia un loop infinito.
Questo perchè useEffect , ripeto , ha lo stesso scopo di componentDidMount, componeteDidUpdate e componentWillUnmount. Essendo componentDidUpdate un metodo che avvia un ciclo di vita il quale viene eseguito ogni volta che un componente riceve nuovi oggetti di scena o si verifica un cambiamento di stato.
Il trucco per correggere questo Bug è quello di passare un array vuoto come secondo parametro ad useEffect:
// useEffect(() => { fetch("https://jsonplaceholder.typicode.com/users") .then(res => res.json()) .then(data => setData(data)); }, []) //
Il suddetto array contiene le cosiddette dipendenze per useEffect, ossia delle variabili da cui lo stesso dipende e da rieseguire.
Se l’array è vuoto l’effetto verrà eseguito una sola volta.
Il codice del tutorial è disponibile sul mio repository Git diviso in branch tanti quanti sono gli argomenti dello stesso.
Se volete Approfondire le conoscenze su Git e GIT hub 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!