Het beheren van de state van een WordPress app – hoe het gegevens verwerkt en organiseert – kan een uitdaging zijn. Naarmate je project groeit, wordt het steeds moeilijker om de gegevensstroom bij te houden en te zorgen voor consistente updates van alle onderdelen. Het WordPress data pakket kan hierbij helpen, omdat het een robuuste oplossing biedt voor het beheren van de state.

Dit artikel gaat in op het WordPress data pakket en onderzoekt de belangrijkste concepten, implementatiestrategieën en best practices.

Kennismaking met het WordPress data pakket

Het WordPress data pakket – officieel @wordpress/data – is een JavaScript (ES2015 en hoger) state management bibliotheek die een voorspelbare en gecentraliseerde manier biedt om de state van applicaties te beheren. De juiste implementatie kan het gemakkelijker maken om complexe gebruikersinterfaces te bouwen en de gegevensstroom in je applicatie af te handelen.

Het WordPress data pakket is geïnspireerd op Redux, een populaire state management bibliotheek in het React ecosysteem.

De Redux homepage, met een paarse header die het Redux logo en de titel bevat, samen met een tagline en een Get Started knop. Daaronder staan vier belangrijke features met iconen. Elke sectie bevat beschrijvende tekst die de mogelijkheden van Redux uitlegt op het gebied van applicatiebeheer, debugging tools en compatibiliteit met het ecosysteem. De bovenste navigatiebalk bevat links naar verschillende andere pagina's op de site en een zoekfunctie.
De officiële Redux website.

De datamodule werkt binnen de WordPress omgeving en biedt het integraties met WordPress specifieke functionaliteit en API’s. Als je bouwt voor de WordPress Block Editor – of het is iets dat je moet ondersteunen – zal het pakket cruciaal zijn bij het beheren van de state ervan. Door dezelfde tools en patterns te gebruiken in je eigen plugins en thema’s, kun je een meer consistente en vertrouwde ontwikkelervaring creëren.

De relatie tussen het pakket en Redux

Hoewel het WordPress data pakket geïnspireerd is op Redux, is het geen directe port. Er zijn veel aanpassingen die passen bij het WordPress ecosysteem, met enkele belangrijke verschillen tussen de twee oplossingen:

  • Het data pakket is ontworpen om naadloos samen te werken met WordPress API’s en functionaliteit, wat vanilla Redux niet kan zonder die aanpassing.
  • Vergeleken met Redux biedt het data pakket een meer gestroomlijnde API. Dit kan het gemakkelijker maken om aan de slag te gaan.
  • In tegenstelling tot Redux heeft het data pakket ingebouwde ondersteuning voor asynchrone acties. Als je met de WordPress REST API werkt, zal dit handig zijn.

Het WordPress data pakket heeft ook enkele vergelijkingen met de REST API. Hoewel ze allebei te maken hebben met gegevensbeheer, dienen ze verschillende doelen:

  • De WordPress REST API biedt een manier om te communiceren met WordPress gegevens via HTTP. Je zult het gebruiken voor externe apps, headless WordPress setups en overal waar je gegevens moet ophalen en manipuleren.
  • Het WordPress data pakket biedt een centrale opslagplaats voor gegevens en UI-state. Het is een manier om gegevensstromen en updates binnen je app af te handelen.

In veel gevallen zul je beide samen gebruiken: de REST API om gegevens op te halen en bij te werken op de server en het WordPress data pakket om die gegevens binnen je applicatie te beheren.

Belangrijkste concepten en terminologie voor het WordPress data pakket

Het WordPress data pakket biedt een intuïtieve manier om gegevens te beheren. Zie dit als gegevens binnen een store. Het vertegenwoordigt de huidige state van je applicatie en kan zowel UI-state (zoals of er een open modal is) als gegevensstate (zoals een lijst met berichten) bevatten.

De pagina Posts van het WordPress dashboard toont een lijst van 106 posts met verschillende filteropties bovenaan. Deze interface toont kolommen voor titel, auteur, categorieën, tags en datum. De linker zijbalk bevat typische WordPress admin navigatie-items naar andere schermen. Zowel gepubliceerde als geplande content is opgenomen in de lijst met berichten.
De state van je berichtgegevens is één gebied dat het WordPress data pakket beheert.

In deze context is een store de centrale hub van het WordPress data pakket. Het bewaart de hele state van de site en biedt de methoden om die state te openen en bij te werken. In WordPress kun je meerdere stores hebben. Elke store is verantwoordelijk voor een specifiek deel van je site.

Om die stores te beheren, heb je een register nodig. Dit centrale object biedt methoden om nieuwe stores te registreren en toegang te krijgen tot je bestaande stores. Een register bevat stores en die stores bevatten de state van je applicatie.

Er zijn een paar manieren om met de state te werken:

  • Acties beschrijven de veranderingen in een state. Dit zijn gewone JavaScript objecten en zijn de enige manier om statesupdates te triggeren. Acties hebben meestal een eigenschap type, die de actie beschrijft. Het kan ook aanvullende gegevens bevatten.
  • Selectors halen specifieke stukken state uit de store. Deze functies geven je toegang tot statesgegevens zonder de noodzaak voor directe interactie met de structuur van de store. Resolvers zijn verwant en behandelen het asynchroon ophalen van gegevens. Je gebruikt deze om ervoor te zorgen dat je toegang hebt tot de benodigde gegevens in een store voordat je een selector uitvoert.
  • Reducers specificeren hoe de state moet veranderen als reactie op acties. Ze nemen de huidige state en een actie als argumenten en retourneren een nieuw statesobject. Controlefuncties laten de reducers complexe async operaties afhandelen zonder neveneffecten.

Je moet deze fundamentele concepten begrijpen, omdat ze allemaal samenwerken om een robuust statesbeheersysteem te maken met stores in het hart.

Stores: de centrale hub van het WordPress data pakket

Stores zijn de containers voor de state van je applicatie en bieden de methoden om ermee te communiceren. Het WordPress data pakket bundelt een paar andere pakketten en elk van deze registreert stores voor de Block directory, Block Editor, core, het bewerken van berichten en meer.

Elke store heeft een unieke namespace, zoals core, core/editor, en core/notices. Externe plugins zullen ook stores registreren, dus je moet unieke namespaces kiezen om conflicten te voorkomen. Stores die je registreert zullen in de meeste gevallen in het standaard register staan.

Dit centrale object heeft een paar verantwoordelijkheden:

  • Registreren van nieuwe stores.
  • Toegang geven tot bestaande stores.
  • Het beheren van subscriptions op statewijzigingen.

Hoewel je niet vaak directe interactie hebt met het register, moet je wel de rol ervan begrijpen in hoe het data pakket het beheer van de state in WordPress orkestreert.

Basis interactie met WordPress data stores

Als je ES2015+ JavaScript gebruikt en je werkt met een WordPress plugin of thema, dan kun je deze als dependency opnemen:

npm install @wordpress/data --save

In je code importeer je de benodigde functies uit het pakket bovenaan het bestand:

import { select, dispatch, subscribe } from '@wordpress/data';

Interactie met bestaande WordPress stores vereist dat je enkele van de functies gebruikt die je importeert. Toegang tot stategegevens met select bijvoorbeeld:

const posts = select('core').getPosts();

Hetzelfde geldt voor het dispatchen van acties:

dispatch('core').savePost(postData);

Subscriptions op statesveranderingen gebruikt een iets ander format, maar het concept is hetzelfde:

subscribe(() => {
  const newPosts = select('core').getPosts();
  // Update your UI based on the new posts
});

Je werkt echter niet altijd met de standaard stores. Vaak werk je met bestaande extra stores of registreer je je eigen stores.

Hoe registreer je een WordPress data store

Het definiëren van de configuratie van je store en het registreren met het WordPress data pakket begint met het importeren van de register functie:

…
import { createReduxStore, register } from '@wordpress/data';
…

Deze neemt een enkel argument – je store descriptor. Vervolgens moet je een default state voor de store definiëren om de standaardwaarden in te stellen:

…
const DEFAULT_STATE = {
  todos: [],
};
…

Maak vervolgens een actions object, definieer een reducer functie om stateupdates af te handelen en maak een selectors object met functies om de stategegevens te benaderen:

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,
};

Om de storeconfiguratie te maken, definieer je deze met het createReduxStore object. Dit initialiseert de acties, selectors, besturing en andere properties voor je store:

const store = createReduxStore('my-plugin/todos', {
  reducer,
  actions,
  selectors,
});

Het minimum dat dit object nodig heeft is een reducer om de vorm van de state te definiëren en hoe deze verandert als reactie op andere acties. Tenslotte registreer je de store door de store descriptor te callen die je hebt gedefinieerd met createReduxStore:

register(store);

Je kunt nu communiceren met je aangepaste store zoals je dat ook met andere zou doen:

import { select, dispatch } from '@wordpress/data';
// Add a new todo
dispatch('my-plugin/todos').addTodo('Learn WordPress data pakket');
// Get all todos
const todos = select('my-plugin/todos').getTodos();

De sleutel in het gebruik van het WordPress data pakket is hoe je de verschillende eigenschappen en objecten gebruikt die tot je beschikking staan.

De vijf properties van de WordPress data store

Een groot deel van het gebruik van het WordPress data pakket gebeurt “achterstevoren” – het definiëren van low-level data store properties vóór de store zelf. Het createReduxStore object is een perfect voorbeeld, omdat het alle definities samenbrengt die je maakt om de descriptor te maken die je gebruikt om een store te registreren:

import { createReduxStore } from '@wordpress/data';
  const store = createReduxStore( 'demo', {
    reducer: ( state = 'OK' ) => state,
    selectors: {
    getValue: ( state ) => state,
    },
  } );

Deze andere properties moeten ook worden ingesteld en geconfigureerd.

1. Actions

Actions zijn de belangrijkste manier om stateveranderingen in je store te triggeren. Het zijn eenvoudige JavaScript objecten die beschrijven wat er moet gebeuren. Daarom kan het een goed idee zijn om deze eerst te maken, omdat je dan kunt bepalen welke states je wilt ophalen.

const actions = {
  addTodo: (text) => ({
    type: 'ADD_TODO',
    text,
  }),
  toggleTodo: (index) => ({
    type: 'TOGGLE_TODO',
    index,
  }),
};

Action creators nemen optionele argumenten aan en geven een object terug dat je kunt doorgeven aan de reducer die je hebt gedefinieerd:

const actions = {
  updateStockPrice: (symbol, newPrice) => {
  return {
    type: 'UPDATE_STOCK_PRICE',
    symbol,
    newPrice
  };
},

Als je een store descriptor doorgeeft, kun je action creators dispatchen en de statewaarde bijwerken:

dispatch('my-plugin/todos').updateStockPrice('¥', '150.37');

Beschouw actie-objecten als instructies voor de reducer over hoe de state te veranderen. Op zijn minst zul je waarschijnlijk create, update, read en delete (CRUD) acties willen definiëren. Het kan ook zijn dat je een apart JavaScript bestand hebt voor actietypen en voor al die typen een object maakt, vooral als je ze als constanten definieert.

2. Reducer

Het is de moeite waard om het hier over de reducer te hebben, vanwege zijn centrale rol naast acties. Het is zijn taak om te specificeren hoe de state moet veranderen als reactie op de instructies die hij krijgt van een actie. Als je het de instructies van de actie en de huidige staat doorgeeft, kan het een nieuw statesobject teruggeven en dit doorgeven aan de chain:

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;
    }
};

Let op dat een reducer een pure functie moet zijn, en het zou de state die het accepteert niet moeten muteren (in plaats daarvan zou het het moeten teruggeven met updates). Reducers en acties hebben in veel opzichten een symbiotische relatie, dus begrijpen hoe ze samenwerken is belangrijk.

3. Selectors

Om toegang te krijgen tot de huidige staat van een geregistreerde store, heb je selectors nodig. Dit is de belangrijkste manier om de staat van je store “bloot te geven” en ze helpen om je componenten los te koppelen van de interne structuur van de store:

const selectors = {
  getTodos: (state) => state.todos,
  getTodoCount: (state) => state.todos.length,
};

Je kunt deze selectors callen met de functie select:

const todoCount = select('my-plugin/todos').getTodoCount();

Een selector stuurt die gegevens echter nergens naartoe: hij onthult ze gewoon en biedt toegang.

Selectors kunnen zoveel argumenten ontvangen als je nodig hebt om toegang te krijgen tot de state. De waarde die wordt geretourneerd is het resultaat van wat die argumenten bereiken binnen de selector die je definieert. Net als bij acties kun je ervoor kiezen om een apart bestand te maken voor al je selectors, omdat het er heel veel kunnen zijn.

4. Controls

Om de uitvoeringsstroom van de functionaliteit van je site te sturen – of de logica erin uit te voeren – gebruik je controls. Deze definiëren het gedrag van uitvoeringsstromen voor je acties. Beschouw ze als de assistenten in het WordPress data pakket, want ze werken als tussenpersonen om de state te verzamelen die moet worden doorgegeven aan resolvers.

Controls handelen ook neveneffecten in je store af, zoals API calls of interacties met browser-API’s. Ze laten je reducers schoon houden terwijl je toch complexe async operaties kunt afhandelen:

const controls = {
  FETCH_TODOS: async () => {
    const response = await fetch('/api/todos');
    return response.json();
  },
};

const actions = {
  fetchTodos: () => ({ type: 'FETCH_TODOS' }),
};

Deze cyclus van gegevens ophalen en terugsturen is cruciaal voor het hele proces. Maar zonder een call van een actie kun je die gegevens niet gebruiken.

5. Resolvers

Selectors geven de state van een store weer, maar sturen die gegevens niet expliciet ergens naartoe. Resolvers gebruiken selectors (en controls) om de gegevens op te halen. Net als besturingselementen kunnen ze ook asynchroon gegevens ophalen.

const resolvers = {
  getTodos: async () => {
    const todos = await controls.FETCH_TODOS();
    return actions.receiveTodos(todos);
  },
};

De resolver zorgt ervoor dat de gegevens waar je om vraagt beschikbaar zijn in de store voordat hij een selector uitvoert. Deze nauwe verbinding tussen de resolver en de selector betekent dat ze namen moeten matchen. Dit is zodat het WordPress data pakket kan begrijpen welke resolver moet worden aangeroepen op basis van de gegevens die je opvraagt.

Bovendien zal de resolver altijd dezelfde argumenten ontvangen die je doorgeeft aan een selector functie en zal hij ook actieobjecten retourneren, opleveren of verzenden.

Foutafhandeling bij het gebruik van het WordPress data pakket

Je moet een goede foutafhandeling implementeren als je werkt met het WordPress data pakket. Als je ervoor kiest om asynchrone operaties uit te voeren, met full stack implementaties werkt of API calls doet, is het nog belangrijker.

Als je bijvoorbeeld acties uitvoert die asynchrone bewerkingen met zich meebrengen, kan een try-catch blok een goede optie zijn:

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>
  );
};

Voor reducers kun je foutacties afhandelen en de state bijwerken:

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;
  }
};

Als je selectors gebruikt, kun je foutcontrole inbouwen om mogelijke problemen af te handelen en dan controleren op fouten in je componenten voordat je de gegevens gebruikt…:

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>
  );
};

De functies useSelect en useDispatch geven je veel mogelijkheden om fouten af te handelen binnen het WordPress data pakket. Met beide functies kun je aangepaste foutmeldingen als argumenten doorgeven.

Het is een goede gewoonte om ervoor te zorgen dat je je error state centraliseert tijdens de eerste configuratie en om foutgrenzen op componentniveau te houden. Het toepassen van foutafhandeling voor loading states zal ook helpen om je code duidelijk en consistent te houden.

Hoe je je WordPress data store kunt integreren met je site

Er is veel dat het WordPress data pakket kan doen om je te helpen bij het beheren van states. Dit alles samenvoegen is ook een praktische overweging. Laten we eens kijken naar een aandelenticker die financiële gegevens in real-time weergeeft en bijwerkt.

De eerste taak is het maken van een opslagplaats voor je gegevens:

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);

Dit proces stelt een standaardstate in die error- en loading state bevat, samen met je acties, reducers en selectors. Zodra je deze hebt gedefinieerd, kun je de store registreren.

De gegevens van de store weergeven

Nu de store er is, kun je een React component maken om de informatie in de store weer te geven:

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>
  );
};

Dit component gebruikt de hooks useSelect en useDispatch (samen met andere) voor het afhandelen van gegevenstoegang, het dispatchen van acties en het levenscyclusbeheer van componenten. Het stelt ook aangepaste error- en loading state berichten in, samen met wat code om de ticker daadwerkelijk weer te geven. Nu dit klaar is, moet je het component registreren bij WordPress.

De component registreren bij WordPress

Zonder registratie bij WordPress kun je de componenten die je maakt niet gebruiken. Dit betekent dat je het moet registreren als een Block, hoewel het ook een widget zou kunnen zijn als je ontwerpt voor Classic Themes. Dit voorbeeld gebruikt een Block.

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
});

Dit proces volgt de typische aanpak voor het registreren van blokken in WordPress en vereist geen speciale implementatie of instellingen.

State updates en gebruikersinteracties beheren

Zodra je het blok hebt geregistreerd, moet je gebruikersinteracties en real-time updates afhandelen. Hiervoor zijn enkele interactieve controls nodig, samen met custom HTML en JavaScript:

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>
  );
};

Voor real-time updates kun je een interval instellen binnen het React component:

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]);

Deze aanpak houdt de gegevens van je component synchroon met je store, terwijl er een duidelijke scheiding van zorgen blijft. Het WordPress data pakket handelt alle state-updates af, waardoor je app consistent blijft.

Rendering op de server

Tot slot kun je server-side rendering instellen om ervoor te zorgen dat de stock data actueel zijn bij het laden van de pagina. Dit vereist enige PHP kennis:

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'
));

Deze aanpak zorgt voor een volledige integratie van je dataopslag met WordPress, waarbij alles wordt afgehandeld, van de eerste render tot real-time updates en gebruikersinteracties.

Samenvatting

Het WordPress data pakket is een complexe maar robuuste manier om applicatiestates voor je projecten te beheren. Achter de belangrijkste concepten ligt een enorm aantal functies, operatoren, argumenten en meer. Onthoud echter dat niet elk stukje data in een globale opslag hoeft te zitten – de lokale componentstate heeft nog steeds een plaats in je code.

Zie jij jezelf het WordPress data pakket regelmatig gebruiken, of heb je een andere methode om de state te beheren? Deel je mening met ons in de reacties hieronder.

Steve Bonisteel Kinsta

Steve Bonisteel is Technical Editor bij Kinsta. Hij begon zijn schrijverscarrière als verslaggever en achtervolgde ambulances en brandweerwagens. Sinds eind jaren negentig schrijft hij over internetgerelateerde technologie.