Sobes.tech
Junior

Quali sono gli hook in React?

sobes.tech AI

Risposta dell'AI

In React, dalla versione 16.8, gli hook consentono ai componenti funzionali di gestire lo stato e di utilizzare i cicli di vita, che prima erano disponibili solo per i componenti di classe.

Ecco i principali hook integrati:

  • useState: Permette di aggiungere uno stato interno a un componente funzionale. Restituisce una coppia: lo stato corrente e una funzione per aggiornarlo.

    // Esempio useState
    import React, { useState } from 'react';
    
    function Counter() {
      const [count, setCount] = useState(0); // Inizializza lo stato a 0
    
      return (
        <div>
          <p>Hai cliccato {count} volte</p>
          <button onClick={() => setCount(count + 1)}>
            Cliccami
          </button>
        </div>
      );
    }
    
  • useEffect: Permette di eseguire effetti collaterali nei componenti funzionali. Simile ai metodi del ciclo di vita dei componenti di classe (componentDidMount, componentDidUpdate, componentWillUnmount).

    // Esempio useEffect
    import React, { useState, useEffect } from 'react';
    
    function Example() {
      const [count, setCount] = useState(0);
    
      // Esegue dopo ogni render
      useEffect(() => {
        document.title = `Hai cliccato ${count} volte`;
      });
    
      // Esegue solo al primo render (array di dipendenze vuoto)
      useEffect(() => {
        console.log('Componente montato');
        return () => {
          console.log('Componente smontato'); // funzione di cleanup
        };
      }, []);
    
      // Esegue quando cambia count
      useEffect(() => {
        console.log('Count è cambiato:', count);
      }, [count]);
    
      return (
        <div>
          <p>Hai cliccato {count} volte</p>
          <button onClick={() => setCount(count + 1)}>
            Cliccami
          </button>
        </div>
      );
    }
    
  • useContext: Permette di sottoscriversi alle modifiche del contesto di React. Accetta un oggetto di contesto, creato con React.createContext().

    // Esempio useContext
    import React, { useContext } from 'react';
    
    const ThemeContext = React.createContext('light'); // Creazione del contesto
    
    function ThemedButton() {
      const theme = useContext(ThemeContext); // Uso del contesto
    
      return (
        <button className={theme}>
          Pulsante nel tema {theme}
        </button>
      );
    }
    
    function App() {
      return (
        <ThemedButton />
      );
    }
    
  • useReducer: Alternativa a useState per gestire stati più complessi, che includono più sotto-valori o quando il prossimo stato dipende dal precedente. Simile ai reducer di Redux.

    // Esempio useReducer
    import React, { useReducer } from 'react';
    
    const initialState = { count: 0 };
    
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return { count: state.count + 1 };
        case 'decrement':
          return { count: state.count - 1 };
        default:
          throw new Error();
      }
    }
    
    function Counter() {
      const [state, dispatch] = useReducer(reducer, initialState);
    
      return (
        <>
          Count: {state.count}
          <button onClick={() => dispatch({ type: 'increment' })}>+</button>
          <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
        </>
      );
    }
    
  • useCallback: Memorizza una funzione di callback. Restituisce una versione memorizzata della callback che cambia solo se una delle dipendenze cambia. Utile per prevenire renderizzazioni inutili di componenti figli passando callback.

    // Esempio useCallback
    import React, { useState, useCallback } from 'react';
    
    function Parent({ addTodo }) {
      const [count, setCount] = useState(0);
    
      const handleAddTodo = useCallback(() => {
        addTodo();
      }, [addTodo]); // Dipendenza da addTodo
    
      return (
        <>
          <button onClick={() => setCount(count + 1)}>
            Incrementa contatore
          </button>
          <Child onAddItem={handleAddTodo} />
        </>
      );
    }
    
    function Child({ onAddItem }) {
      console.log('Child si renderizza'); // Si renderizza solo se onAddItem cambia
      return <button onClick={onAddItem}>Aggiungi elemento</button>;
    }
    
  • useMemo: Memorizza un valore calcolato. Restituisce un valore memorizzato che viene ricalcolato solo se una delle dipendenze cambia. Utile per migliorare le prestazioni in calcoli costosi.

    // Esempio useMemo
    import React, { useMemo, useState } from 'react';
    
    function expensiveCalculation(num) {
      console.log('Esecuzione calcolo costoso...');
      for (let i = 0; i < 1000000; i++) {
        num += 1;
      }
      return num;
    }
    
    function Example() {
      const [count, setCount] = useState(0);
      const [todos, setTodos] = useState([]);
    
      const calculation = useMemo(() => expensiveCalculation(count), [count]); // Dipendenza da count
    
      const addTodo = () => {
        setTodos(t => [...t, "Nuovo Todo"]);
      };
    
      return (
        <div>
          <div>
            <h2>Le mie Todo</h2>
            {todos.map((todo, index) => (
              <p key={index}>{todo}</p>
            ))}
            <button onClick={addTodo}>Aggiungi Todo</button>
          </div>
          <hr />
          <div>
            Contatore: {count}
            <button onClick={() => setCount(c => c + 1)}>+</button>
            <h2>Calcolo costoso</h2>
            {calculation}
          </div>
        </div>
      );
    }
    
  • useRef: Permette di creare un ref mutabile che persiste per tutta la durata del ciclo di vita del componente. Spesso usato per accedere a elementi DOM o memorizzare qualsiasi valore mutabile che non causa render.

    // Esempio useRef
    import React, { useRef, useEffect } from 'react';
    
    function TextInputWithFocusButton() {
      const inputEl = useRef(null); // Creazione del ref
    
      const onButtonClick = () => {
        // current punta all'elemento DOM montato
        inputEl.current.focus();
      };
    
      return (
        <>
          <input ref={inputEl} type="text" /> // Assegnazione del ref
          <button onClick={onButtonClick}>Focalizza campo di input</button>
        </>
      );
    }
    
  • useImperativeHandle: Configura il descrittore dell'istanza che viene fornito al genitore quando si usa ref. Permette al componente figlio di esporre determinati metodi al componente genitore. Usato insieme a forwardRef.

    // Esempio useImperativeHandle
    import React, { useRef, useImperativeHandle, forwardRef } from 'react';
    
    const MyInput = forwardRef((props, ref) => {
      const inputRef = useRef();
    
      useImperativeHandle(ref, () => ({
        focusInput: () => {
          inputRef.current.focus();
        }
      }));
    
      return <input ref={inputRef} {...props} />;
    });
    
    function ParentComponent() {
      const inputRef = useRef();
    
      const handleFocus = () => {
        inputRef.current.focusInput(); // Chiamata al metodo del componente figlio
      };
    
      return (
        <div>
          <MyInput ref={inputRef} />
          <button onClick={handleFocus}>Focalizza tramite Parent</button>
        </div>
      );
    }
    
  • useLayoutEffect: Identico a useEffect, ma viene eseguito in modo sincrono dopo tutte le mutazioni del DOM. Utile per leggere dati dal DOM o eseguire mutazioni che devono essere fatte prima che il browser ridisegni lo schermo. Può bloccare il rendering.

    // Esempio useLayoutEffect
    import React, { useLayoutEffect, useRef } from 'react';
    
    function Tooltip({ children, text }) {
      const ref = useRef(null);
    
      useLayoutEffect(() => {
        if (ref.current) {
          // Calcoli basati sul DOM prima del rendering
          const { top, height } = ref.current.getBoundingClientRect();
          console.log(`L'elemento si trova a ${top} con altezza ${height}`);
        }
      }, [children]);
    
      return (
        <span ref={ref} title={text}>
          {children}
        </span>
      );
    }
    
  • useDebugValue: Mostra un'etichetta personalizzata per gli hook negli strumenti di sviluppo di React. Usato nei custom hook per il debugging.

    // Esempio useDebugValue
    import React, { useState, useDebugValue } from 'react';
    
    function useFriendStatus(friendID) {
      const [isOnline, setIsOnline] = useState(null);
    
      // Mostra 'Friend Status: Offline' o 'Friend Status: Online' negli strumenti di sviluppo
      useDebugValue(isOnline === true ? 'Online' : 'Offline');
    
      // ... logica per ottenere lo stato dell'amico
    
      return isOnline;
    }
    
    function FriendListItem({ friend }) {
      const isOnline = useFriendStatus(friend.id);
    
      return (
        <li style={{ color: isOnline ? 'green' : 'black' }}>
          {friend.name} - {isOnline ? 'Online' : 'Offline'}
        </li>
      );
    }
    

Oltre agli hook integrati, esistono anche custom hook. Sono funzioni JavaScript il cui nome inizia con "use". Permettono di riutilizzare la logica di stato tra componenti. Un custom hook può chiamare altri hook.

// Esempio di custom hook
import { useState, useEffect } from 'react';

function useWindowSize() {
  const [size, setSize] = useState([window.innerWidth, window.innerHeight]);

  useEffect(() => {
    const handleResize = () => {
      setSize([window.innerWidth, window.innerHeight]);
    };
    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return size;
}

function MyComponent() {
  const [width, height] = useWindowSize(); // Uso del custom hook

  return (
    <p>Dimensione finestra: {width}x{height}</p>
  );
}

È importante rispettare le regole degli hook:

  1. Chiamare gli hook solo al livello superiore del componente React o di un custom hook. Non chiamarli all’interno di cicli, condizioni o funzioni annidate.
  2. Chiamare gli hook solo da componenti funzionali React o da custom hook. Non chiamarli da funzioni JavaScript normali o metodi di classi.