Introduzione a React, parte 3
Pubblicato: 2020-07-30La scorsa settimana abbiamo visto che un componente React non è altro che la rappresentazione grafica di (parte) dello stato dell'applicazione. Nell'esempio che abbiamo implementato, un componente mostrava il valore di un contatore insieme a due pulsanti che permettevano di aumentare o diminuire tale valore. Il trucco era semplice: gli props di scena di un componente possono essere sia dati che funzioni.
Finora, le due idee principali che abbiamo imparato dalla Parte 1 e dalla Parte 2 di questo tutorial sono:
- Un componente React è una funzione pura che riceve un insieme di proprietà e genera l'HTML richiesto per il rendering.
- Le proprietà che riceve un componente React possono essere (a) i dati che dovrebbe mostrare nell'interfaccia utente e (b) funzioni che, una volta connesse come callback agli eventi DOM, modificano lo stato della nostra app.
Bene, in questa terza parte amplieremo ulteriormente le nostre conoscenze su questa architettura e impareremo come utilizzare i negozi WordPress . Con loro, possiamo definire lo stato della nostra applicazione in modo pulito, testabile e indipendente dall'interfaccia utente.
Estendere il nostro esempio
Nella seconda parte di questo tutorial abbiamo creato un contatore con un paio di pulsanti per aumentarne o diminuirne il valore. Per fare ciò, abbiamo aggiunto una variabile mutabile nel file index.js insieme a una funzione per mutarla. Il nostro componente contatore :
export const Counter = ( { value, onIncrease, onDecrease } ) => ( <div> <div>Counter: <strong>{ value }</strong></div> <button onClick={ onIncrease }>+</button> <button onClick={ onDecrease }>-</button> </div> );potrebbe quindi ricevere tutti gli oggetti di scena di cui aveva bisogno:
// Import dependencies import { render } from '@wordpress/element'; import { Counter } from './components/counter'; // Store value let value = 0; function setValue( newValue ) { value = newValue; } // Render component in DOM const wrapper = document.getElementById( 'react-example-wrapper' ); render( <Counter value={ value } onIncrease={ () => setValue( value + 1 ) } onDecrease={ () => setValue( value - 1 ) } />, wrapper );Bene, oggi impareremo come possiamo implementare la gestione statale in modo corretto e migliore. Ma prima, vorrei puntare su un esempio più complesso: espandiamo la nostra app in modo che possa avere più contatori contemporaneamente e vediamo come gestirli tutti utilizzando un negozio WordPress. Il risultato finale (che implementeremo nel prossimo post) sarà simile a questo:

Definizione dello stato della nostra applicazione
Quando affronti un problema come questo, la prima cosa a cui dovresti pensare è come gestirai lo stato della tua app . E, supponendo che tu possa leggere e scrivere lo stato solo usando le funzioni, il modo più semplice per farlo è pensare alla sua API: cioè le funzioni che ti permettono di interrogarlo e aggiornarlo . Nel nostro caso potrei suggerire:
- Aggiunta di un nuovo contatore
- Cancellazione contatore x
- Imposta il valore del contatore x
- Ottieni un elenco di tutti i contatori della nostra app
- Ottieni il valore del contatore x
ma potresti voler utilizzare un approccio diverso (ad esempio, potresti voler creare una funzione per incrementare un determinato contatore e una funzione diversa per decrementarlo).
Una volta definita questa interfaccia, dovresti pensare alle informazioni di cui hai bisogno per tenere traccia di questo stato e, in particolare, alla struttura dei dati che utilizzerai per farlo. Nel nostro esempio, vogliamo tenere traccia di più contatori e, per ogni contatore x , vogliamo conoscerne il valore. Quali strutture di dati potrebbero aiutarci in questa impresa?
A seconda di cosa sia questa x , potresti voler usare una struttura di dati o un'altra. Se, ad esempio, x è l'indice del contatore (ovvero, si desidera essere in grado di recuperare il valore del primo , secondo o terzo contatore), una matrice di numeri potrebbe essere sufficiente. Se vuoi che x sia un identificatore di contatore univoco, potresti voler utilizzare un approccio diverso. Ad esempio, potresti voler utilizzare un dizionario con coppie id/valore:
const counters = { ae13a: 0, f18bb: 3, e889a: 1, 8b1d3: -5, };o una matrice di oggetti:
const counters = [ { id: 'ae13a', value: 0 }, { id: 'f18bb', value: 3 }, { id: 'e889a', value: 1 }, { id: '8b1d3', value: -5 }, ];Se definisci prima l'interfaccia (setter e getter, se vuoi) per manipolare e accedere allo stato della tua app, il modo in cui implementi lo stato stesso è una scatola nera. Ciò significa che puoi modificare il negozio stesso in qualsiasi momento e le cose funzioneranno come previsto fintanto che mantieni l'API.
Creazione di un negozio basato su Redux con WordPress
Il modulo dati di WordPress ti consente di definire uno o più negozi per gestire lo stato della tua applicazione. Per creare un nuovo negozio utilizzando questo pacchetto, devi semplicemente utilizzare la funzione registerStore con i seguenti argomenti:
- Un nome che identifica in modo univoco il tuo negozio
- Un oggetto
selectorscon tutti i getter per recuperare i dati dall'archivio - Un oggetto
actionscon funzione che attiva richieste di aggiornamento (ne parleremo più avanti in questo post) - Una funzione di
reducerresponsabile dell'aggiornamento dello stato quando vengono attivate determinate azioni
Vediamo un esempio reale, vero? Continuando con l'esempio della scorsa settimana, crea una nuova cartella store in src . Quindi crea un file index.js al suo interno con il seguente codice:
// Load dependencies import { registerStore } from '@wordpress/data'; import reducer from './reducer'; import * as actions from './actions'; import * as selectors from './selectors'; registerStore( 'react-example/counters', { actions, reducer, selectors, } ); Lo snippet è abbastanza semplice, vero? Importiamo semplicemente la funzione registerStore di cui stavamo parlando e alcune dipendenze che non abbiamo ancora creato ( reducer , actions e selectors ) e registriamo il nuovo negozio. Nota come abbiamo chiamato il nostro negozio per assicurarci che sia univoco: abbiamo combinato il nome del nostro plugin ( react-example ) con una parola che definisce di cosa tratta il negozio ( counters ). Vai tranquillo!
Azioni in un negozio
Una delle cose di cui (in linea di principio) ogni negozio ha bisogno è un insieme di funzioni che ci consentano di modificarne lo stato. Nel nostro esempio, le azioni saranno in src/store/actions.js :
export function addCounter( counterId ) { return { type: 'ADD_COUNTER', counterId, }; } export function removeCounter( counterId ) { return { type: 'REMOVE_COUNTER', counterId, }; } export function setCounterValue( counterId, value ) { return { type: 'SET_COUNTER_VALUE', counterId, value, }; }Come previsto, il nostro negozio ha tre azioni per aggiornare il suo stato:

-
addCounter: una funzione che aggiunge nuovi contatori nel nostro negozio. L'unico argomento che richiede è l'ID del nuovo contatore. -
removeCounter: una funzione che rimuove un contatore esistente. Ancora una volta, l'unico argomento necessario è l'ID del contatore che vogliamo rimuovere. -
setCounterValue: una funzione che imposta un nuovo valore su un determinato contatore. Ovviamente questa funzione accetta due argomenti: l'ID del contatore da aggiornare e il suo nuovo valore.
Ora, se guardi da vicino ogni azione potresti rimanere sorpreso: nessuna di queste azioni sembra aggiornare nulla. Invece, restituiscono oggetti. Cosa sta succedendo qui?
In Redux (e gli store di WordPress sono basati su Redux), le azioni non modificano direttamente un negozio. Generano invece un oggetto che segnala una "richiesta di aggiornamento". Queste richieste seguono sempre lo stesso schema: sono un oggetto con un attributo type che identifica in modo univoco la richiesta e tutte le proprietà aggiuntive necessarie per applicare correttamente l'aggiornamento richiesto.
Vediamo ora come si può effettivamente aggiornare lo stato...
Implementazione del riduttore per aggiornare lo stato di un negozio
Se le azioni del negozio rappresentano solo una richiesta di aggiornamento, abbiamo bisogno di qualcuno o qualcosa che aggiorni effettivamente lo stato del nostro negozio quando tale richiesta viene inviata. Ecco a cosa serve un riduttore.
Un riduttore è una funzione che rileva lo stato corrente della nostra applicazione e un'azione inviata da qualcuno e aggiorna lo stato applicando l'aggiornamento richiesto .
Nella sezione precedente abbiamo visto che il nostro negozio ha tre azioni, quindi il nostro riduttore deve essere in grado di applicarle ciascuna:
import { omit } from 'lodash'; export default function reducer( state = {}, action ) { switch ( action.type ) { case 'ADD_COUNTER': return { ...state, [ action.counterId ]: 0, }; case 'REMOVE_COUNTER': return omit( state, action.counterId ); case 'SET_COUNTER_VALUE': return { ...state, [ action.counterId ]: action.value, }; } return state; } Come puoi vedere, il riduttore riceve lo state precedente (che, tra l'altro, per impostazione predefinita è l'oggetto vuoto {} ) e l'azione inviata con le informazioni per aggiornare lo stato. Il corpo del riduttore è abbastanza semplice:
- Inizia con un'istruzione
switchper discernere il tipo di aggiornamento (action.type) che dovremmo eseguire:- Se è
ADD_COUNTER, genera un nuovo oggettostatecon una nuova chiaveaction.counterIdcon il valore del contatore impostato su0. - Se è
REMOVE_COUNTER, genera un nuovo oggetto distatesenza la chiaveaction.counterId. - Se è
SET_COUNTER_VALUE, genera un nuovo oggettostatein cui il valore inaction.counterIdè ora impostato suaction.value.
- Se è
La cosa più importante qui è rendersi conto che un riduttore è (e deve essere) una pura funzione . Ciò significa che qualsiasi modifica che apportiamo allo stato attuale implica la costruzione di un nuovo stato . In nessun caso, quindi, dovresti mutare lo stato precedente.
Selettori in un negozio
Ora che sai come aggiornare lo stato del tuo negozio, tutto ciò che devi imparare è come interrogarlo. Basta definire un selectors.js con le funzioni di query di cui hai bisogno:
export function getCounterIds( state ) { return Object.keys( state ); } export function getCounterValue( state, counterId ) { return state[ counterId ]; } e basta! Abbastanza ovvio, vero? I selettori del negozio sono funzioni che accettano (almeno) un argomento (lo state del negozio) e restituiscono un valore specifico . Ovviamente, i selettori possono avere più argomenti se devi restituire un valore specifico dall'interno del tuo negozio.
Nel nostro caso, ad esempio, abbiamo creato due selettori:
-
getCounterIdsrestituisce una matrice di identificatori di contatore. Poiché abbiamo implementato il negozio utilizzando un dizionario/oggetto, siamo semplicemente interessati a restituire il suoObject.keys. -
getCounterValuerestituisce il valore specifico di un determinato contatore. Questa funzione accetta due argomenti (lostatecorrente della nostra app e l'ID del contatore a cui siamo interessati) e restituisce il valore richiesto.
Come testare il nostro negozio
Per verificare che lo store funzioni correttamente, apri il file src/index.js e import :
// Import dependencies import { render } from '@wordpress/element'; import './store'; import { Counter } from './components/counter'; ... Quindi, trascrivi il codice (usando npm run build ) e vai al tuo browser. Apri i suoi Strumenti per sviluppatori e usa la console JavaScript per digitare alcuni comandi:
dispatch = wp.data.dispatch( 'react-example/counters' ); select = wp.data.select( 'react-example/counters' ); dispatch.addCounter( 'a' ); dispatch.addCounter( 'b' ); dispatch.addCounter( 'c' ); dispatch.setCounterValue( 'a', 3 ); select.getCounterIds(); // Array(3) [ "a", "b", "c" ] select.getCounterValue( 'a' ); // 3 Utilizzando le funzioni di dispatch e select di WordPress sarai in grado di vedere che il negozio funziona come previsto. E un consiglio bonus: esiste un'estensione sia per Firefox che per Chrome chiamata Redux DevTools che ti consente di vedere correttamente i tuoi negozi Redux:

Prossimi passi
La verità è che il post di oggi è stato un po' più lungo di quanto mi aspettassi, quindi non saremo in grado di vedere come possiamo utilizzare questo negozio per potenziare la nostra interfaccia utente (ancora). Ma spero che la spiegazione ti aiuti a capire come funzionano gli store di WordPress e come puoi usarli per gestire lo stato dei tuoi plugin.
La prossima settimana andremo avanti con l'esempio di oggi e collegheremo i nostri componenti React al negozio in modo che (a) ciò che vediamo nell'interfaccia utente sia alimentato dallo stato memorizzato nel negozio e (b) le interazioni dell'utente con l'interfaccia utente aggiornino il negozio ( così come l'interfaccia utente stessa).
Ma, solo per essere sicuro che tu ti senta a tuo agio con tutto ciò che ti ho mostrato oggi, lascia che ti proponga alcuni compiti: modifica il negozio che abbiamo implementato oggi in modo che i dati vengano archiviati come segue:
const counters = [ { id: 'ae13a', value: 0 }, { id: 'f18bb', value: 3 }, { id: 'e889a', value: 1 }, { id: '8b1d3', value: -5 }, ];e non ci fa più un dizionario. Tieni presente che potrebbe essere necessario aggiornare i selettori, le azioni e il riduttore per farlo funzionare! Condividerò una soluzione la prossima settimana
Immagine in primo piano di Annie Theby su Unsplash.
