Gestire lo stato di qualsiasi applicazione WordPress, ovvero il modo in cui gestisce e organizza i dati, può essere una vera sfida. Man mano che un progetto cresce, tenere traccia del flusso di dati e garantire aggiornamenti coerenti tra i vari componenti diventa sempre più difficile. Il pacchetto dati di WordPress può essere d’aiuto in questo senso, in quanto fornisce una soluzione solida per la gestione dello stato.
Questo articolo analizza il pacchetto dati di WordPress, esplorandone i concetti chiave, le strategie di implementazione e le best practice.
Il pacchetto dati di WordPress
Il pacchetto dati di WordPress (ufficialmente @wordpress/data
) è una libreria di gestione dello stato in JavaScript (ES2015 e versioni successive) che fornisce un modo prevedibile e centralizzato per gestire lo stato delle applicazioni. La giusta implementazione può facilitare la costruzione di interfacce utente complesse e la gestione del flusso di dati all’interno dell’applicazione.
Il pacchetto dati di WordPress si ispira a Redux, una libreria di gestione dello stato molto diffusa nell’ecosistema React.
In questo caso, il modulo dati funziona all’interno dell’ambiente WordPress e fornisce integrazioni con funzionalità e API specifiche di WordPress. Se costruite per l’editor a blocchi di WordPress, o se è qualcosa che dovete supportare, il pacchetto sarà fondamentale per gestire il suo stato. Utilizzando gli stessi strumenti e modelli in plugin e temi, è possibile creare un’esperienza di sviluppo più coerente e familiare.
Il rapporto del pacchetto con Redux
Sebbene il pacchetto dati di WordPress tragga ispirazione da Redux, non si tratta di un porting diretto. Sono state introdotte diverse modifiche per adattarlo all’ecosistema WordPress ed esistono alcune differenze chiave tra le due soluzioni:
- Il pacchetto dati è stato progettato per funzionare perfettamente con le API e le funzionalità di WordPress, cosa che Redux vanilla non può fare senza questo adattamento.
- Rispetto a Redux, il pacchetto dati fornisce un’API più snella. Questo può rendere più facile iniziare a lavorare.
- A differenza di Redux, il pacchetto dati include un supporto integrato per le azioni asincrone. Se lavorate con l’API REST di WordPress, vi sarà utile.
Il pacchetto dati di WordPress può essere paragonato all’API REST. Sebbene entrambi si occupino della gestione dei dati, hanno scopi diversi:
- L’API REST di WordPress fornisce un modo per interagire con i dati di WordPress tramite HTTP. La utilizzerete per le applicazioni esterne, per le configurazioni di WordPress headless e ovunque abbiate bisogno di recuperare e manipolare i dati.
- Il pacchetto dati di WordPress fornisce un archivio centralizzato per i dati e lo stato dell’interfaccia utente. È un modo per gestire il flusso di dati e gli aggiornamenti all’interno della vostra applicazione.
In molti casi, userete entrambi insieme: l’API REST per recuperare e aggiornare i dati sul server e il pacchetto dati WordPress per gestire quei dati all’interno della vostra applicazione.
Concetti chiave e terminologia del pacchetto dati WordPress
Il pacchetto dati WordPress offre un modo intuitivo di gestire lo stato. Questo si riferisce ai dati all’interno di uno store, o archivio. Rappresenta la condizione attuale della vostra applicazione e può includere sia lo stato dell’interfaccia utente (ad esempio se c’è una finestra modale aperta) che lo stato dei dati (ad esempio un elenco di post).
In questo contesto, uno store è l’hub centrale del pacchetto dati di WordPress. Contiene l’intero stato del sito e fornisce i metodi per accedervi e aggiornarlo. In WordPress potete avere più store. Ognuno di essi sarà responsabile di un’area specifica del vostro sito.
Per gestire questi store, avete bisogno di un registro. Questo oggetto centrale fornisce i metodi per registrare nuovi store e accedere a quelli esistenti. Il registro contiene gli store, e questi store contengono lo stato dell’applicazione.
Ci sono diversi modi per lavorare con lo stato:
- Le azioni (actions) descrivono le modifiche a uno stato. Sono oggetti JavaScript semplici e sono l’unico modo per attivare gli aggiornamenti dello stato. Le azioni hanno in genere una proprietà
type
che descrive l’azione. Può anche includere dati aggiuntivi. - I selettori (selectors) estraggono pezzi specifici di stato dallo store. Queste funzioni permettono di accedere ai dati dello stato senza dover interagire direttamente con la struttura dell’archivio. I risolutori sono correlati e gestiscono il recupero asincrono dei dati. Si usano per assicurarsi di poter accedere ai dati richiesti in un archivio prima di eseguire un selettore.
- I riduttori (reducers) specificano come lo stato deve cambiare in risposta alle azioni. Prendono come argomenti lo stato corrente e un’azione e restituiscono un nuovo oggetto stato. Le funzioni di controllo permettono ai riduttori di gestire operazioni asincrone complesse senza effetti collaterali.
È necessario comprendere questi concetti fondamentali, poiché tutti lavorano insieme per creare un solido sistema di gestione dello stato con gli store al suo centro.
Gli store: l’hub centrale del pacchetto dati di WordPress
Gli store sono i contenitori dello stato delle applicazioni e forniscono i metodi per interagire con esso. Il pacchetto dati di WordPress raggruppa alcuni altri pacchetti e ognuno di questi registra gli archivi per la directory dei blocchi, l’editor a blocchi, il core, la modifica dei post e altro ancora.
Ogni archivio avrà un namespace unico, come core
, core/editor
e core/notices
. Anche i plugin di terze parti registreranno gli store, quindi è necessario scegliere namespace unici per evitare conflitti. In ogni caso, nella maggior parte dei casi gli store registrati saranno presenti nel registro predefinito.
Questo oggetto centrale ha alcune responsabilità:
- Registrare nuovi store.
- Fornire l’accesso agli store esistenti.
- Gestire le sottoscrizioni alle modifiche di stato.
Anche se spesso non si ha un’interazione diretta con il registro, è necessario comprendere il suo ruolo nel modo in cui il pacchetto dati orchestra la gestione dello stato in WordPress.
Interazione di base con gli archivi di dati di WordPress
Se utilizzate JavaScript ES2015+ e state lavorando con un plugin o un tema di WordPress, potete includerlo come dipendenza:
npm install @wordpress/data --save
Nel vostro codice, importerete le funzioni necessarie dal pacchetto all’inizio del file:
import { select, dispatch, subscribe } from '@wordpress/data';
L’interazione con gli store WordPress esistenti richiede l’utilizzo di alcune delle funzioni importate. Accedere ai dati di stato con select
, ad esempio:
const posts = select('core').getPosts();
Lo stesso vale per il dispatching delle azioni:
dispatch('core').savePost(postData);
La sottoscrizione ai cambiamenti di stato utilizza un formato leggermente diverso, ma il concetto è lo stesso:
subscribe(() => {
const newPosts = select('core').getPosts();
// Update your UI based on the new posts
});
Tuttavia, non lavorerete sempre con gli store predefiniti. Spesso lavorerete con archivi aggiuntivi esistenti o registrerete i vostri.
Come registrare un archivio dati WordPress
Per definire la configurazione del vostro store e registrarlo con il pacchetto dati di WordPress è necessario importare la funzione register
:
…
import { createReduxStore, register } from '@wordpress/data';
…
Questa funzione accetta un unico argomento: il descrittore del vostro store. Successivamente, dovrete definire uno stato predefinito per lo store per impostare i suoi valori predefiniti:
…
const DEFAULT_STATE = {
todos: [],
};
…
Quindi, create un oggetto actions
, definite una funzione reducer
per gestire gli aggiornamenti dello stato e create un oggetto selectors
con le funzioni per accedere ai dati dello stato:
const actions = {
addTodo: (text) => ({
type: 'ADD_TODO',
text,
}),
};
const reducer = (state = DEFAULT_STATE, action) => {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, { text: action.text, completed: false }],
};
default:
return state;
}
};
const selectors = {
getTodos: (state) => state.todos,
};
Per creare la configurazione dello store, definitela utilizzando l’oggetto createReduxStore
. In questo modo verranno inizializzate le azioni, i selettori, i controlli e le altre proprietà dello store:
const store = createReduxStore('my-plugin/todos', {
reducer,
actions,
selectors,
});
Il minimo che serve a questo oggetto è un riduttore per definire la forma dello stato e come cambia in risposta ad altre azioni. Infine, registrate lo store, chiamando il descrittore dello store che avete definito con createReduxStore
:
register(store);
Ora potete interagire con il vostro store personalizzato come fareste con gli altri:
import { select, dispatch } from '@wordpress/data';
// Add a new todo
dispatch('my-plugin/todos').addTodo('Learn WordPress data package');
// Get all todos
const todos = select('my-plugin/todos').getTodos();
Il segreto dell’utilizzo del pacchetto dati di WordPress sta nell’uso delle diverse proprietà e degli oggetti a disposizione.
Le cinque proprietà dello store dati di WordPress
Gran parte dell’utilizzo del pacchetto dati di WordPress avviene “al contrario”, ovvero definendo le proprietà di basso livello dello store di dati prima dello store stesso. L’oggetto createReduxStore
è un esempio perfetto, in quanto riunisce tutte le definizioni fatte per creare il descrittore usato per registrare un archivio:
import { createReduxStore } from '@wordpress/data';
const store = createReduxStore( 'demo', {
reducer: ( state = 'OK' ) => state,
selectors: {
getValue: ( state ) => state,
},
} );
Anche queste altre proprietà devono essere impostate e configurate.
1. Le azioni
Le azioni sono il modo principale per attivare i cambiamenti di stato nel vostro store. Sono semplici oggetti JavaScript che descrivono ciò che deve accadere. Per questo motivo, può essere una buona idea crearle per prime, in quanto potete decidere quali stati volete recuperare.
const actions = {
addTodo: (text) => ({
type: 'ADD_TODO',
text,
}),
toggleTodo: (index) => ({
type: 'TOGGLE_TODO',
index,
}),
};
I creatori di azioni accettano argomenti opzionali e restituiscono un oggetto da passare al riduttore che avete definito:
const actions = {
updateStockPrice: (symbol, newPrice) => {
return {
type: 'UPDATE_STOCK_PRICE',
symbol,
newPrice
};
},
Se inserite un descrittore dello store, potete inviare i creatori di azioni e aggiornare il valore dello stato:
dispatch('my-plugin/todos').updateStockPrice('¥', '150.37');
Considerate gli oggetti azione come istruzioni per il riduttore su come apportare modifiche allo stato. Come minimo, probabilmente vorrete definire azioni di creazione, aggiornamento, lettura e cancellazione (CRUD). Potreste anche avere un file JavaScript separato per i tipi di azione e creare un oggetto per tutti questi tipi, soprattutto se li definite come costanti.
2. Il riduttore
Vale la pena di parlare del riduttore in questa sede, visto il suo ruolo centrale accanto alle azioni. Il suo compito è quello di specificare come lo stato deve cambiare in risposta alle istruzioni che riceve da un’azione. Se gli passate le istruzioni dell’azione e lo stato corrente, può restituire un nuovo oggetto di stato e passarlo lungo la catena:
const reducer = (state = DEFAULT_STATE, action) => {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, { text: action.text, completed: false }],
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map((todo, index) =>
index === action.index ? { ...todo, completed: !todo.completed } : todo
),
};
default:
return state;
}
};
Notate che un riduttore deve essere una funzione pura e non deve mutare lo stato che accetta (piuttosto deve restituirlo aggiornato). I riduttori e le azioni hanno una relazione simbiotica sotto molti aspetti, quindi è importante capire come lavorano insieme.
3. I selettori
Per accedere allo stato corrente da un archivio registrato, avete bisogno di selettori. È il modo principale per “esporre” lo stato dello store e aiutano a mantenere i vostri componenti disaccoppiati dalla struttura interna dello store:
const selectors = {
getTodos: (state) => state.todos,
getTodoCount: (state) => state.todos.length,
};
Potete chiamare questi selettori con la funzione select
:
const todoCount = select('my-plugin/todos').getTodoCount();
Tuttavia, un selettore non invia i dati da nessuna parte: si limita a rivelarli e a fornirne l’accesso.
I selettori possono ricevere tutti gli argomenti necessari per accedere con precisione allo stato. Il valore che restituisce è il risultato di ciò che questi argomenti ottengono all’interno del selettore che definite. Come per le azioni, potreste scegliere di avere un file separato per contenere tutti i vostri selettori, dato che potrebbero essere molti.
4. Controlli
Per guidare il flusso di esecuzione delle funzionalità del vostro sito – o per eseguire la logica al suo interno – si utilizzano i controlli. Questi definiscono il comportamento dei flussi di esecuzione delle azioni. Sono considerati come gli assistenti del pacchetto dati di WordPress, in quanto lavorano come intermediari per raccogliere lo stato da passare ai risolutori.
I controlli gestiscono anche gli effetti collaterali del vostro store, come le chiamate API o le interazioni con le API del browser. Permettono di mantenere puliti i riduttori, pur consentendo di gestire operazioni asincrone complesse:
const controls = {
FETCH_TODOS: async () => {
const response = await fetch('/api/todos');
return response.json();
},
};
const actions = {
fetchTodos: () => ({ type: 'FETCH_TODOS' }),
};
Questo ciclo di recupero e restituzione dei dati è fondamentale per l’intero processo. Ma senza una chiamata da parte di un’azione, non potrete utilizzare quei dati.
5. I resolver
I selettori espongono lo stato di uno store, ma non inviano esplicitamente i dati da nessuna parte. I resolver incontrano i selettori (e i controlli) per recuperare i dati. Come i controlli, possono anche gestire il recupero asincrono dei dati.
const resolvers = {
getTodos: async () => {
const todos = await controls.FETCH_TODOS();
return actions.receiveTodos(todos);
},
};
Il resolver si assicura che i dati richiesti siano disponibili nell’archivio prima di eseguire un selettore. Questa stretta connessione tra il resolver e il selettore comporta la necessaria corrispondenza dei nomi. In questo modo il pacchetto dati di WordPress può capire quale risolutore invocare in base ai dati richiesti.
Inoltre, il resolver riceverà sempre gli stessi argomenti che vengono passati in una funzione del selettore e restituirà, renderà o invierà oggetti azione.
Gestione degli errori quando si utilizza il pacchetto dati di WordPress
È necessario implementare una corretta gestione degli errori quando si lavora con il pacchetto dati di WordPress. Se scegliete di gestire operazioni asincrone, di lavorare con implementazioni full stack o di effettuare chiamate API, è ancora più importante.
Ad esempio, se eseguite azioni che comportano operazioni asincrone, un blocco try-catch potrebbe essere una buona opzione:
const StockUpdater = () => {
// Get the dispatch function
const { updateStock, setError, clearError } = useDispatch('my-app/stocks');
const handleUpdateStock = async (stockId, newData) => {
try {
// Clear any existing errors
clearError();
// Attempt to update the stock
await updateStock(stockId, newData);
} catch (error) {
// Dispatch an error action if something goes wrong
setError(error.message);
}
};
return (
<button onClick={() => handleUpdateStock('AAPL', { price: 150 })}>
Update Stock
</button>
);
};
Per i riduttori, potete gestire le azioni di errore e aggiornare lo stato:
const reducer = (state = DEFAULT_STATE, action) => {
switch (action.type) {
// ... other cases
case 'FETCH_TODOS_ERROR':
return {
...state,
error: action.error,
isLoading: false,
};
default:
return state;
}
};
Quando utilizzate i selettori, potete includere un controllo degli errori per gestire potenziali problemi e verificare la presenza di errori nei vostri componenti prima di utilizzare i dati:
const MyComponent = () => {
// Get multiple pieces of state including error information
const { data, isLoading, error } = useSelect((select) => ({
data: select('my-app/stocks').getStockData(),
isLoading: select('my-app/stocks').isLoading(),
error: select('my-app/stocks').getError()
}));
// Handle different states
if (isLoading) {
return <div>Loading...</div>;
}
if (error) {
return (
<div className="error-message">
<p>Error loading stocks: {error.message}</p>
<button onClick={retry}>Try Again</button>
</div>
);
}
return (
<div>
{/* Your normal component render */}
</div>
);
};
Le funzioni useSelect
e useDispatch
permettono di gestire gli errori all’interno del pacchetto dati di WordPress. Con entrambe le funzioni potete inserire messaggi di errore personalizzati come argomenti.
È buona norma assicurarsi di centralizzare lo stato di errore durante la configurazione iniziale e di mantenere i confini degli errori a livello di componente. Anche la gestione degli errori per gli stati di caricamento vi aiuterà a mantenere il codice chiaro e coerente.
Come integrare lo store dati di WordPress con il proprio sito
Il pacchetto dati di WordPress può fare molto per aiutarvi a gestire gli stati. Mettere insieme tutto questo è anche una considerazione pratica. Prendiamo ad esempio un sito web che mostra e aggiorna i dati finanziari in tempo reale.
Il primo compito è quello di creare uno store per i dati:
import { createReduxStore, register } from '@wordpress/data';
const DEFAULT_STATE = {
stocks: [],
isLoading: false,
error: null,
};
const actions = {
fetchStocks: () => async ({ dispatch }) => {
dispatch({ type: 'FETCH_STOCKS_START' });
try {
const response = await fetch('/api/stocks');
const stocks = await response.json();
dispatch({ type: 'RECEIVE_STOCKS', stocks });
} catch (error) {
dispatch({ type: 'FETCH_STOCKS_ERROR', error: error.message });
}
},
};
const reducer = (state = DEFAULT_STATE, action) => {
switch (action.type) {
case 'FETCH_STOCKS_START':
return { ...state, isLoading: true, error: null };
case 'RECEIVE_STOCKS':
return { ...state, stocks: action.stocks, isLoading: false };
case 'FETCH_STOCKS_ERROR':
return { ...state, error: action.error, isLoading: false };
default:
return state;
}
};
const selectors = {
getStocks: (state) => state.stocks,
getStocksError: (state) => state.error,
isStocksLoading: (state) => state.isLoading,
};
const store = createReduxStore('my-investing-app/stocks', {
reducer,
actions,
selectors,
});
register(store);
Questo processo imposta uno stato predefinito che include gli stati di errore e di caricamento, oltre alle azioni, ai riduttori e ai selettori. Una volta definiti questi elementi, potete registrare lo store.
Visualizzare i dati dello store
Una volta creato lo store, potete creare un componente React per visualizzare le informazioni al suo interno:
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect } from '@wordpress/element';
const StockTicker = () => {
const stocks = useSelect((select) => select('my-investing-app/stocks').getStocks());
const error = useSelect((select) => select('my-investing-app/stocks').getStocksError());
const isLoading = useSelect((select) => select('my-investing-app/stocks').isStocksLoading());
const { fetchStocks } = useDispatch('my-investing-app/stocks');
useEffect(() => {
fetchStocks();
}, []);
if (isLoading) {
return <p>Loading stock data...</p>;
}
if (error) {
return <p>Error: {error}</p>;
}
return (
<div className="stock-ticker">
<h2>Stock Ticker</h2>
<ul>
{stocks.map((stock) => (
<li key={stock.symbol}>
{stock.symbol}: ${stock.price}
</li>
))}
</ul>
</div>
);
};
Questo componente integra gli hook useSelect
e useDispatch
(oltre ad altri) per gestire l’accesso ai dati, il dispatching delle azioni e la gestione del ciclo di vita del componente. Inoltre, imposta messaggi di errore e di stato di caricamento personalizzati, oltre a un po’ di codice per visualizzare effettivamente il ticker. Una volta fatto questo, bisogna registrare il componente con WordPress.
Registrare il componente con WordPress
Senza la registrazione in WordPress, non potrete utilizzare i componenti che avete creato. Ciò significa registrarlo come blocco, anche se potrebbe trattarsi di un widget se progettate per i temi classici. Questo esempio utilizza un blocco.
import { registerBlockType } from '@wordpress/blocks';
import { StockTicker } from './components/StockTicker';
registerBlockType('my-investing-app/stock-ticker', {
title: 'Stock Ticker',
icon: 'chart-line',
category: 'widgets',
edit: StockTicker,
save: () => null, // This will render dynamically
});
Questo processo seguirà l’approccio tipico per la registrazione dei blocchi all’interno di WordPress e non richiede alcuna implementazione o configurazione particolare.
Gestire gli aggiornamenti di stato e le interazioni con gli utenti
Una volta registrato il blocco, dovete gestire le interazioni degli utenti e gli aggiornamenti in tempo reale. A tal fine saranno necessari alcuni controlli interattivi, oltre a HTML e JavaScript personalizzati:
const StockControls = () => {
const { addToWatchlist, removeFromWatchlist } = useDispatch('my-investing-app/stocks');
return (
<div className="stock-controls">
<button onClick={() => addToWatchlist('AAPL')}>
Add Apple to Watchlist
</button>
<button onClick={() => removeFromWatchlist('AAPL')}>
Remove from Watchlist
</button>
</div>
);
};
Per gli aggiornamenti in tempo reale, potete impostare un intervallo all’interno del componente React:
useEffect(() => {
const { updateStockPrice } = dispatch('my-investing-app/stocks');
const interval = setInterval(() => {
stocks.forEach(stock => {
fetchStockPrice(stock.symbol)
.then(price => updateStockPrice(stock.symbol, price));
});
}, 60000);
return () => clearInterval(interval);
}, [stocks]);
Questo approccio permette di mantenere i dati del vostro componente sincronizzati con il vostro store, mantenendo una chiara separation of concerns. Il pacchetto dati di WordPress gestirà tutti gli aggiornamenti di stato, garantendo così la coerenza della vostra applicazione.
Rendering lato server
Infine, potete impostare il rendering lato server per assicurarvi che i dati dello stock siano aggiornati al caricamento della pagina. Questo richiede una certa conoscenza di PHP:
function my_investing_app_render_stock_ticker($attributes, $content) {
// Fetch the latest stock data from your API
$stocks = fetch_latest_stock_data();
ob_start();
?>
<div class="stock-ticker">
<h2>Stock Ticker</h2>
<ul>
<?php foreach ($stocks as $stock) : ?>
<li><?php echo esc_html($stock['symbol']); ?>: $<?php echo esc_html($stock['price']); ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php
return ob_get_clean();
}
register_block_type('my-investing-app/stock-ticker', array(
'render_callback' => 'my_investing_app_render_stock_ticker'
));
Questo approccio fornisce un’integrazione completa del vostro archivio dati con WordPress, gestendo tutto, dal rendering iniziale agli aggiornamenti in tempo reale e alle interazioni con gli utenti.
Riepilogo
Il pacchetto dati di WordPress è un modo complesso ma robusto per gestire gli stati dell’applicazione per i vostri progetti. Al di là dei concetti chiave, esiste una serie di funzioni, operatori, argomenti e molto altro ancora. Ricordate, però, che non tutti i dati devono trovarsi in un archivio globale: lo stato locale dei componenti ha ancora un posto nel vostro codice.
Pensate di utilizzare regolarmente il pacchetto dati di WordPress o avete un altro metodo per gestire lo stato? Condividete con noi le vostre opinioni nei commenti qui sotto.
Lascia un commento