如何使用WordPress数据包管理应用程序状态

如何使用WordPress数据包管理应用程序状态

管理任何 WordPress 应用程序的状态(如何处理和组织数据)都是一项挑战。随着项目的发展,跟踪数据流并确保各组件更新的一致性变得越来越困难。WordPress 数据包可以在这方面提供帮助,因为它为状态管理提供了一个强大的解决方案。

本文将介绍 WordPress 数据包,探讨其关键概念、实施策略和最佳实践。

介绍WordPress数据包

WordPress 数据包(正式名称为@wordpress/data)是一个 JavaScript(ES2015 及更高版本)状态管理库,为管理应用程序状态提供了一种可预测的集中方式。正确的实现有助于更轻松地构建复杂的用户界面,并处理整个应用程序的数据流。

WordPress 数据包的灵感来自 React 生态系统中流行的状态管理库 Redux

Redux 官方网站
Redux 官方网站

在这里,数据模块在 WordPress 环境中运行,并提供与 WordPress 特定功能和 API 的集成。如果您为 WordPress 区块编辑器进行构建(或者您必须支持它),那么该软件包对于管理其状态至关重要。通过在自己的插件和主题中使用相同的工具和模式,您可以创建更加一致和熟悉的开发体验。

数据包与Redux的关系

虽然 WordPress 数据包从 Redux 中汲取了灵感,但它并不是直接移植过来的。为了适应 WordPress 生态系统,数据包做了很多调整,两种解决方案之间也存在一些关键差异:

  • 数据包旨在与 WordPress 应用程序接口和功能无缝协作,而 Vanilla Redux 则无法做到这一点。
  • 与 Redux 相比,数据包提供了更精简的 API。这可以让你更容易上手。
  • 与 Redux 不同,数据包内置了对异步操作的支持。如果你使用 WordPress REST API,这将非常有用。

WordPress 数据包与 REST API 也有一些可比性。虽然它们都涉及数据管理,但用途不同:

  • WordPress REST API提供了一种通过HTTP与WordPress数据交互的方式。您可以将其用于外部应用程序、无头 WordPress 设置以及任何需要检索和操作数据的地方。
  • WordPress 数据包为数据和用户界面状态提供了一个集中存储空间。它是在应用程序中处理数据流和更新的一种方式。

在许多情况下,您会同时使用这两种方法:使用 REST API 在服务器上获取和更新数据,使用 WordPress 数据包在应用程序中管理这些数据。

WordPress数据包的关键概念和术语

WordPress 数据包提供了一种直观的状态管理方式。这是指存储中的数据。它代表了应用程序的当前状态,可以包括用户界面状态(如是否有打开的模态)和数据状态(如帖子列表)。

文章数据的状态
文章数据的状态是 WordPress 数据包管理的一个方面。

在这种情况下,存储是 WordPress 数据包的中心枢纽。它保存网站的整个状态,并提供访问和更新该状态的方法。在 WordPress 中,您可以拥有多个存储空间。每个存储将负责网站的特定区域。

为了管理这些存储,您需要一个注册表。这个中心对象提供了注册新存储和访问现有存储的方法。注册表将保存存储,而这些存储将保存应用程序状态。

处理状态有几种方法:

  • 操作描述状态的变化。这些是普通的 JavaScript 对象,是触发状态更新的唯一方式。操作通常会有一个 type 属性,用于描述操作。它还可能包含其他数据。
  • 选择器从存储中提取特定的状态片段。通过这些函数,你可以访问状态数据,而无需直接与存储结构交互。解析器(Resolvers)与之相关,用于处理异步数据获取。使用这些功能可以确保在运行选择器之前访问存储中的所需数据。
  • 还原器指定状态应如何改变以响应操作。还原器将当前状态和操作作为参数,并返回一个新的状态对象。控制函数可让还原器在不产生副作用的情况下处理复杂的异步操作。

你需要理解这些基本概念,因为它们共同作用,创建了一个以存储为核心的强大的状态管理系统。

存储:WordPress数据包的中心枢纽

存储是应用程序状态的容器,并提供与之交互的方法。WordPress 数据包捆绑了其他几个包,每个包都为块目录、区块编辑器、核心、文章编辑等注册了存储。

每个存储都有一个独特的命名空间,如corecore/ editor 和 core/notices。第三方插件也会注册存储空间,因此需要选择唯一的命名空间以避免冲突。无论如何,你注册的存储空间在大多数情况下都会存在于默认注册表中。

这个中心对象有几个职责:

  • 注册新商店。
  • 提供对现有商店的访问。
  • 管理对状态变化的订阅。

虽然您不会经常与注册表进行直接交互,但您确实需要了解它在数据包如何协调整个 WordPress 的状态管理中的作用。

与WordPress数据存储的基本交互

如果您使用 ES2015+JavaScript,并且正在使用 WordPress 插件或主题,您可以将其作为依赖项:

npm install @wordpress/data --save

在代码中,您将从文件顶部的软件包中导入必要的函数:

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

与现有 WordPress 商店交互时,需要使用一些导入的功能。例如,使用 select 访问状态数据:

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

调度操作也是如此:

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

订阅状态更改使用的格式略有不同,但概念相同:

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

不过,您并不总是使用默认商店。通常情况下,您需要使用现有的附加商店或注册自己的商店。

如何注册WordPress数据存储

定义您的商店配置并将其注册到 WordPress 数据包中,首先要导入 register 函数:

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

它只需要一个参数–你的商店描述符。接下来,你应该为商店定义一个默认状态,以设置其默认值:

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

接下来,创建一个 actions 对象,定义一个用于处理状态更新的 reducer 函数,并创建一个带有访问状态数据函数的 selectors 对象:

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

要创建商店配置,请使用 createReduxStore 对象进行定义。这将初始化商店的操作、选择器、控件和其他属性:

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

该对象最起码需要一个还原器来定义状态的形状,以及状态如何随其他操作而变化。最后,通过调用 createReduxStore 定义的存储描述符来注册存储:

register(store);

现在,您可以像与其他人一样与自定义商店进行互动:

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

分解WordPress数据存储的五个属性

使用 WordPress 数据包的大部分工作都是“逆向”进行的–在存储本身之前定义底层数据存储属性。createReduxStore 对象就是一个很好的例子,因为它汇集了所有定义,以便创建用于注册存储的描述符:

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

这些其他属性也需要设置和配置。

1. 动作

动作是触发商店状态变化的主要方式。它们是简单的 JavaScript 对象,描述了应该发生的事情。因此,最好先创建这些动作,因为您可以决定要检索哪些状态。

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

动作创建器采用可选参数,并将返回一个对象,以便传递给您定义的还原器:

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

如果传入存储描述符,就可以分派动作创建器并更新状态值:

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

将动作对象视为指示还原器如何更改状态的指令。至少,您可能需要定义创建、更新、读取和删除(CRUD)操作。您也可以为动作类型创建一个单独的 JavaScript 文件,并为所有这些类型创建一个对象,尤其是当您将它们定义为常量时。

2. 还原器

这里值得谈谈还原器,因为它与动作一起扮演着核心角色。它的工作是指定状态应该如何变化,以响应从动作中得到的指令。如果将动作指令和当前状态传递给它,它就能返回一个新的状态对象,并将其沿链传递:

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

需要注意的是,还原器必须是一个纯函数,它不应该改变它所接受的状态(相反,它应该返回更新后的状态)。还原器和操作在很多方面都是共生关系,因此掌握它们如何协同工作非常重要。

3. 选择器

为了从注册存储访问当前状态,你需要选择器。这是“暴露”商店状态的主要方式,有助于保持组件与商店内部结构的分离:

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

您可以使用 select 函数调用这些选择器:

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

然而,选择器不会将数据发送到任何地方:它只是显示数据并提供访问。

选择器可以接收尽可能多的参数,以便准确地访问状态。它返回的值是这些参数在你定义的选择器中实现的结果。和操作一样,你可能会选择用一个单独的文件来保存所有的选择器,因为可能会有很多这样的选择器。

4. 控件

指导网站功能的执行流程或执行其中的逻辑,就是使用控件的地方。这些控件定义了操作执行流程的行为。将这些控件视为 WordPress 数据包中的助手,因为它们是收集状态传递给解析器的中间人。

控件还能处理商店中的副作用,例如 API 调用或与浏览器 API 的交互。通过它们,您可以保持还原器的清洁,同时还能处理复杂的异步操作:

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

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

这种获取和返回数据的循环对整个流程至关重要。但是,如果没有动作的调用,就无法使用这些数据。

5. 解析器

选择器会显示存储的状态,但不会明确地将数据发送到任何地方。解析器与选择器(和控件)连接,以检索数据。与控件一样,它们也可以处理异步数据获取。

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

在运行选择器之前,解析器会确保您所要求的数据在存储区中可用。解析器和选择器之间的这种紧密联系意味着它们需要匹配名称。这样,WordPress 数据包就能根据您请求的数据了解要调用哪个解析器。

此外,解析器将始终接收您传入选择器函数的相同参数,并返回、生成或分派动作对象。

使用WordPress数据包时的错误处理

在使用 WordPress 数据包时,您必须执行适当的错误处理。如果您选择处理异步操作、使用全栈部署或进行 API 调用,那么这一点就更加重要。

例如,如果您分派的操作涉及异步操作,那么 try-catch 块就是一个不错的选择:

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

对于还原器,可以处理错误操作并更新状态:

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

在使用选择器时,您可以加入错误检查功能来处理潜在问题,然后在使用数据前检查组件中是否存在错误:

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

useSelectuseDispatch 函数为您提供了在 WordPress 数据包中处理错误的强大功能。通过这两个函数,您可以将自定义错误信息作为参数传递。

良好的做法是确保在初始配置时集中管理错误状态,并将错误边界保持在组件级别。对加载状态采用错误处理也有助于保持代码的清晰和一致。

如何将WordPress数据存储与网站集成

WordPress 数据包可以帮助您管理状态。将所有这些整合在一起也是一个实际的考虑因素。让我们来看一个实时显示和更新财务数据的股票行情器。

首要任务是为数据创建一个存储空间:

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

此过程会设置一个默认状态,其中包括错误和加载状态,以及操作、还原器和选择器。定义好这些后,就可以注册商店了。

显示商店数据

有了商店,您就可以创建一个 React 组件来显示其中的信息:

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

该组件引入了 useSelectuseDispatch 钩子(以及其他钩子),用于处理数据访问、调度操作和组件生命周期管理。它还设置了自定义错误和加载状态消息,以及一些实际显示行情的代码。有了这些,您现在需要向 WordPress 注册组件。

在WordPress中注册组件

如果不在WordPress中注册,您就无法使用您创建的组件。这意味着要将其注册为一个块(Block),不过如果您为经典主题设计的话,也可以将其注册为一个小部件(widget)。本示例使用的是区块。

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

这个过程将沿用在 WordPress 中注册区块的典型方法,不需要任何特殊的实施或设置。

管理状态更新和用户交互

一旦注册了区块,就必须处理用户交互和实时更新。这需要一些交互式控件以及自定义 HTML 和 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>
  );
};

对于实时更新,您可以在 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]);

这种方法既能保持组件数据与商店数据同步,又能明确区分关注点。WordPress 数据包将处理所有的状态更新,从而保证应用程序的一致性。

服务器端呈现

最后,您可以设置服务器端呈现,以确保页面加载时库存数据是最新的。这需要一定的 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'
));

这种方法可将您的数据存储与 WordPress 完全集成,处理从初始呈现到实时更新和用户交互的所有事务。

小结

WordPress 数据包是管理项目应用程序状态的一种复杂而强大的方法。除了关键概念之外,还有函数、运算符、参数等等。不过请记住,并不是所有数据都需要放在全局存储中–本地组件状态在你的代码中仍有一席之地。

你是经常使用 WordPress 数据包,还是有其他管理状态的方法?请在下面的评论中与我们分享您的观点。

评论留言