Come iniziare con Redux per la gestione dello stato JavaScript

0
244

Redux è uno strumento di gestione dello stato, creato specificamente per le applicazioni JavaScript lato client che dipendono fortemente da dati complessi e API esterne e forniscono ottimi strumenti per sviluppatori che semplificano il lavoro con i dati.

Cosa fa Redux?< /h2>

In poche parole, Redux è un archivio dati centralizzato. Tutti i dati dell'applicazione sono archiviati in un unico oggetto di grandi dimensioni. Redux Devtools rende questo facile da visualizzare:

Questo stato è immutabile, che all'inizio è un concetto strano, ma ha senso per alcuni motivi. Se vuoi modificare lo stato, devi inviare un'azione, che fondamentalmente prende alcuni argomenti, forma un payload e lo invia a Redux. Redux passa lo stato corrente a una funzione di riduzione, che modifica lo stato esistente e restituisce un nuovo stato che sostituisce quello corrente e attiva un ricaricamento dei componenti interessati. Ad esempio, potresti avere un riduttore per aggiungere un nuovo elemento a un elenco o rimuoverne o modificarne uno già esistente.

Farlo in questo modo significa che non otterrai mai alcun comportamento indefinito con lo stato di modifica dell'app a piacimento. Inoltre, poiché esiste una registrazione di ogni azione e di ciò che è cambiato, consente il debug del viaggio nel tempo, in cui è possibile scorrere indietro lo stato dell'applicazione per eseguire il debug di ciò che accade con ogni azione (proprio come una cronologia git).

Redux può essere usato con qualsiasi framework frontend, ma è comunemente usato con React, ed è quello su cui ci concentreremo qui. Sotto il cofano, Redux utilizza l'API Context di React, che funziona in modo simile a Redux ed è utile per le app semplici se vuoi rinunciare del tutto a Redux. Tuttavia, i Devtools di Redux sono fantastici quando si lavora con dati complessi e in realtà sono più ottimizzati per evitare rerender non necessari.

Pubblicità

Se stai usando TypeScript, le cose sono molto più complicate per ottenere Redux rigorosamente digitato. Ti consigliamo invece di seguire questa guida, che utilizza le azioni typesafe per gestire le azioni e i riduttori in modo adatto ai tipi.

Struttura Il tuo progetto

Innanzitutto, ti consigliamo di disporre la struttura delle cartelle. Questo dipende da te e dalle preferenze di stile del tuo team, ma ci sono fondamentalmente due modelli principali utilizzati dalla maggior parte dei progetti Redux. Il primo è semplicemente suddividere ogni tipo di file (action, reducer, middleware, side-effect) nella propria cartella, in questo modo:

store/actions/reducers/sagas/middleware/index.js

Tuttavia, questo non è il massimo, poiché spesso avrai bisogno sia di un file di azione che di un riduttore per ogni funzione che aggiungi. È meglio unire le cartelle delle azioni e dei riduttori e suddividerle per funzionalità. In questo modo, ogni azione e il relativo riduttore sono nello stesso file. You

store/features/todo/etc/sagas/middleware/root-reducer.js root-action.js index.js

Questo pulisce le importazioni, poiché ora puoi importare sia le azioni che i riduttori in la stessa affermazione usando:

import { todosActions, todosReducer } from 'store/features/todos'

Sta a te decidere se vuoi mantenere il codice Redux nella sua cartella (/store negli esempi precedenti) o integrarlo nella cartella radice src della tua app. Se stai già separando il codice per componente e stai scrivendo molte azioni e riduttori personalizzati per ciascun componente, potresti voler unire le cartelle /features/ e /components/ e memorizzare i componenti JSX insieme al codice riduttore.< /p>

Se stai utilizzando Redux con TypeScript, puoi aggiungere un file aggiuntivo in ciascuna cartella delle funzionalità per definire i tuoi tipi.

Installazione e configurazione di Redux

Installa Redux e React-Redux da NPM:

npm install redux react-redux

Probabilmente vorrai anche redux-devtools:

npm install –save-dev redux-devtools

La prima cosa che vorrai creare è il tuo negozio. Salva questo come /store/index.js

import { createStore } from 'redux' import rootReducer from './root-reducer' const store = createStore(rootReducer) export default store; Pubblicità

Ovviamente, il tuo negozio diventerà più complicato di così quando aggiungerai cose come componenti aggiuntivi per effetti collaterali, middleware e altre utilità come il router connesso a reazione, ma questo è tutto ciò che è richiesto per ora. Questo file prende il root reducer e chiama createStore() utilizzandolo, che viene esportato per l'utilizzo da parte dell'app.

Poi, creeremo una semplice funzione per l'elenco delle cose da fare. Probabilmente vorrai iniziare definendo le azioni richieste da questa funzione e gli argomenti che le vengono passati. Crea una cartella /features/todos/ e salva quanto segue come type.js:

export const ADD = 'ADD_TODO' export const DELETE = 'DELETE_TODO' export const EDIT = 'EDIT_TODO'

Questo definisce alcune costanti stringa per i nomi delle azioni. Indipendentemente dai dati che stai passando, ogni azione avrà una proprietà di tipo, ovvero una stringa univoca che identifica l'azione.

Non è necessario disporre di un file di tipo come questo, poiché è sufficiente digitare il nome della stringa dell'azione, ma è meglio per l'interoperabilità farlo in questo modo. Ad esempio, potresti avere todos.ADD e promemoria.ADD nella stessa app, il che ti evita il fastidio di digitare _TODO o _REMINDER ogni volta che fai riferimento a un'azione per quella funzione.

Quindi, salva quanto segue as /store/features/todos/actions.js:

import * as type from './types.js' export const addTodo = text => ({ type: types.ADD, text }) export const deleteTodo = id => ({ type: types.DELETE, id }) export const editTodo = (id, text) => ({ tipo: tipi.EDIT, id, testo })

Questo definisce alcune azioni usando i tipi dalle costanti di stringa, disponendo gli argomenti e la creazione del payload per ognuno. Queste non devono essere completamente statiche, in quanto sono funzioni—un esempio che potresti utilizzare è l'impostazione di un CUID di runtime per determinate azioni.

Pubblicità

La parte più complicata di codice, e dove implementerai la maggior parte della tua logica aziendale, è nei riduttori. Questi possono assumere molte forme, ma l'impostazione più comunemente utilizzata è con un'istruzione switch che gestisce ogni caso in base al tipo di azione. Salva questo come reducer.js:

import * as type from './types.js' const initialState = [ { text: 'Hello World', id: 0 } ] export default function todos(state = initialState, action) { switch (action.type) { case types .ADD: return [ …state, { id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1, text: action.text } ] case type.DELETE: return state.filter(todo => todo.id !== action.id ) case types.EDIT: return state.map(todo => todo.id === action.id ? { .. .todo, text: action.text } : todo ) default: return state } }

Lo stato viene passato come argomento e ogni caso restituisce una versione modificata dello stato. In questo esempio, ADD_TODO aggiunge un nuovo elemento allo stato (con un nuovo ID ogni volta), DELETE_TODO rimuove tutti gli elementi con l'ID specificato e EDIT_TODO mappa e sostituisce il testo dell'elemento con l'ID specificato.

< p>Anche lo stato iniziale dovrebbe essere definito e passato alla funzione reducer come valore predefinito per la variabile di stato. Ovviamente, questo non definisce l'intera struttura dello stato di Redux, ma solo la sezione state.todos.

Questi tre file sono generalmente separati in app più complesse, ma se lo desideri, puoi anche definire tutti in un unico file, assicurati solo di importare ed esportare correttamente.

Con quella funzione completata, colleghiamola a Redux (e alla nostra app). In /store/root-reducer.js, importa todosReducer (e qualsiasi altro riduttore di funzionalità dalla cartella /features/), quindi passalo a combineReducers(), formando un riduttore radice di primo livello che viene passato allo store. Qui è dove imposterai lo stato root, assicurandoti di mantenere ogni funzionalità sul proprio ramo.

import { combineReducers } from 'redux'; importa todosReducer da './features/todos/reducer'; const rootReducer = combineReducers({ todos: todosReducer }) esporta rootReducer predefinito

Utilizzo di Redux in React

Ovviamente, niente di tutto questo è utile se non è collegato a React. Per fare ciò, dovrai racchiudere l'intera app in un componente Provider. Ciò garantisce che lo stato e gli hook necessari vengano trasmessi a ogni componente della tua app.

Annuncio

In App.js o index.js, ovunque tu abbia la tua funzione di rendering root, avvolgi la tua app in un <Provider> e passalo allo store (importato da /store/index.js) come prop:

import React da 'react'; import ReactDOM da 'react-dom'; // Importazione installazione Redux { Provider } da 'react-redux'; import store, { history } from './store'; ReactDOM.render(     <Provider store={store}> <App/>     </Provider>    , document.getElementById('root'));

Ora sei libero di usare Redux nei tuoi componenti. Il metodo più semplice è con componenti funzionali e ganci. Ad esempio, per inviare un'azione, utilizzerai l'hook useDispatch() , che ti consente di chiamare direttamente le azioni, ad es. dispatch(todosActions.addTodo(text)).

Il seguente contenitore ha un input connesso allo stato React locale, che viene utilizzato per aggiungere una nuova attività allo stato ogni volta che si fa clic su un pulsante:

import React, { useState } from 'react'; import './Home.css'; import { TodoList } from “components' import { todosActions } from 'store/features/todos' import { useDispatch } from 'react-redux' function Home() {   const dispatch = useDispatch(); const [text, setText] = useState(“”); function handleClick() {     dispatch(todosActions.addTodo(text)); setText(“”); }   function handleChange(e: React.ChangeEvent<HTMLInputElement>) {     setText(e.target.value); }  return (     <div className=”App”>       <header className=”App-header”>        <input type=”text” value={text} onChange={handleChange} />        <button onClick ={handleClick}>           Aggiungi New Todo        </button>        <TodoList />      </header>     </div>   ); } esporta la Home predefinita;

Quindi, quando vuoi utilizzare i dati archiviati nello stato, usa l'hook useSelector. Ciò richiede una funzione che seleziona parte dello stato da utilizzare nell'app. In questo caso, imposta la variabile post sull'elenco corrente di cose da fare. Questo viene quindi utilizzato per eseguire il rendering di un nuovo elemento da fare per ogni voce in state.todos.

import React from 'react'; import { useSelector } from 'store' import { Container, List, ListItem, Title } from './styles' function TodoList() {  const posts = useSelector(state => state.todos)   return (    > Continua List>         {posts.map(({ id, title }) => (          <ListItem key={title}>            <Title>{title} : {id}</Title>    ;      < /List     < )}       </Elenco>     </Contenitore>   ); } esporta TodoList predefinito;

Puoi effettivamente creare funzioni di selezione personalizzate per gestirlo per te, salvate nella cartella /features/ proprio come azioni e riduttori.

Una volta che hai impostato e capito tutto, potresti voler esamina la configurazione di Redux Devtools, la configurazione di middleware come Redux Logger o un router connesso a reazione o l'installazione di un modello di effetti collaterali come Redux Sagas.