Salta al contenuto principale

Gestire lo Stato tra due Componenti in React

Profile picture for user luca77king

In React, uno dei concetti chiave che permette di costruire applicazioni moderne è la gestione dello stato. Molte volte, lavorando con un'applicazione complessa, può capitare di dover manipolare oggetti o dati tra diversi componenti. Questo articolo esplorerà come farlo in maniera efficiente, esplorando diverse tecniche e best practices per gestire lo stato tra i componenti in modo che possano interagire senza problemi. Scopriremo insieme i vari modi in cui possiamo far fluire i dati da un componente all'altro, come evitare problemi di sincronizzazione e come mantenere il codice chiaro e ben strutturato.

In React, ogni componente può avere un proprio stato locale. Questo stato, che è rappresentato da un oggetto, determina come un componente si comporta e come renderizza la UI. Tuttavia, se il nostro stato deve essere condiviso tra più componenti, possiamo utilizzare diverse soluzioni per gestire questa condivisione.

Quando un componente ha bisogno di manipolare lo stato che appartiene a un altro componente, si parla di comunicazione tra componenti. React fornisce vari metodi per farlo, ma scegliere il metodo giusto dipende dalla struttura dell'applicazione e dalla complessità dei dati da gestire.

Passaggio di Dati tra Componenti con Props

La maniera più semplice per passare dati tra componenti in React è tramite props. Le props sono un meccanismo per passare dati dai componenti genitori ai componenti figli. Questo approccio è ideale per oggetti e dati che non devono essere modificati direttamente dal componente figlio, ma solo utilizzati per il rendering.

Esempio:

const ParentComponent = () => {
    const user = { name: "Luca", age: 30 };
    
    return <ChildComponent user={user} />;
};

const ChildComponent = ({ user }) => {
    return <div>{user.name} is {user.age} years old.</div>;
};

In questo caso, il componente genitore ParentComponent passa l'oggetto user come prop al componente figlio ChildComponent. Il figlio può quindi utilizzare queste informazioni per renderizzare la UI, ma non può modificare il valore dell'oggetto user direttamente.

Modificare lo Stato da un Componente Figlio: Utilizzare Funzioni di Callback

In molte situazioni, i componenti figli devono modificare lo stato che appartiene ai genitori. React non permette che un componente figlio modifichi direttamente lo stato di un genitore, ma fornisce un modo per farlo tramite funzioni di callback. Queste funzioni sono passate dai genitori ai figli come props e vengono invocate dai figli per aggiornare lo stato del genitore.

Esempio:

const ParentComponent = () => {
    const [user, setUser] = useState({ name: "Luca", age: 30 });

    const updateUser = (newUser) => {
        setUser(newUser);
    };

    return <ChildComponent user={user} updateUser={updateUser} />;
};

const ChildComponent = ({ user, updateUser }) => {
    const handleClick = () => {
        updateUser({ name: "Marco", age: 35 });
    };

    return (
        <div>
            <div>{user.name} is {user.age} years old.</div>
            <button onClick={handleClick}>Change Name</button>
        </div>
    );
};

Nel caso sopra, ParentComponent ha uno stato locale user e una funzione updateUser che aggiorna tale stato. Questa funzione viene passata come prop al ChildComponent, che la invoca per modificare lo stato del genitore quando l'utente clicca il pulsante.

Utilizzare Context per Condividere Dati tra Componenti

Nel caso di una applicazione più complessa, quando ci sono molti componenti che necessitano di accedere o modificare lo stesso stato, passare dati tramite props può diventare ingombrante e difficile da gestire. Una soluzione elegante in questi casi è usare il Context API di React.

Il Context API permette di creare uno "store" globale che può essere condiviso tra tutti i componenti, evitando di passare manualmente props a tutti i livelli della gerarchia di componenti.

Esempio:

const UserContext = React.createContext();

const ParentComponent = () => {
    const [user, setUser] = useState({ name: "Luca", age: 30 });

    return (
        <UserContext.Provider value={{ user, setUser }}>
            <ChildComponent />
        </UserContext.Provider>
    );
};

const ChildComponent = () => {
    const { user, setUser } = useContext(UserContext);

    const handleClick = () => {
        setUser({ name: "Marco", age: 35 });
    };

    return (
        <div>
            <div>{user.name} is {user.age} years old.</div>
            <button onClick={handleClick}>Change Name</button>
        </div>
    );
};

In questo esempio, il UserContext è creato tramite React.createContext e viene usato per condividere lo stato user e la funzione setUser tra i vari componenti. Il Provider di UserContext avvolge i componenti figli, che possono accedere e manipolare il contesto tramite useContext.

Uso di State Management Esterni (Redux, Zustand, Recoil)

Quando l'applicazione diventa davvero grande e il contesto o il passaggio di props non è più sufficiente, è possibile usare una libreria di gestione dello stato esterno come Redux, Zustand o Recoil. Queste librerie permettono di gestire lo stato globale dell'applicazione in modo centralizzato, riducendo la complessità e migliorando la manutenibilità del codice.

Redux è una delle soluzioni più popolari, e consiste in un store globale che contiene tutto lo stato dell'applicazione. I componenti possono quindi leggere dallo store e inviare azioni per modificare lo stato.

Esempio di base con Redux:

// Actions
const setUser = (user) => ({ type: "SET_USER", payload: user });

// Reducer
const userReducer = (state = { name: "", age: 0 }, action) => {
    switch (action.type) {
        case "SET_USER":
            return { ...state, ...action.payload };
        default:
            return state;
    }
};

// Store
const store = createStore(userReducer);

// Componenti
const ParentComponent = () => {
    const dispatch = useDispatch();

    const handleClick = () => {
        dispatch(setUser({ name: "Marco", age: 35 }));
    };

    return <button onClick={handleClick}>Change Name</button>;
};

const ChildComponent = () => {
    const user = useSelector((state) => state);

    return <div>{user.name} is {user.age} years old.</div>;
};

Conclusioni

Gestire oggetti e dati tra componenti in React può sembrare complicato, ma con la giusta conoscenza degli strumenti messi a disposizione dalla libreria, è possibile farlo in modo molto efficiente. Se l'applicazione è relativamente piccola, l'uso delle props e delle funzioni di callback è sufficiente. Tuttavia, per applicazioni più complesse, il Context API o librerie di gestione dello stato come Redux possono fare la differenza, evitando il passaggio manuale delle props attraverso livelli di componenti annidati.

Ricorda che la chiave è mantenere il flusso dei dati chiaro e ben strutturato, scegliendo la soluzione giusta per le tue necessità in base alla complessità dell'applicazione.