如何使用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 外掛或主題,您可以將其作為依賴項:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
npm install @wordpress/data --save
npm install @wordpress/data --save
npm install @wordpress/data --save

在程式碼中,您將從檔案頂部的軟體包中匯入必要的函式:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { select, dispatch, subscribe } from '@wordpress/data';
import { select, dispatch, subscribe } from '@wordpress/data';
import { select, dispatch, subscribe } from '@wordpress/data';

與現有 WordPress 商店互動時,需要使用一些匯入的功能。例如,使用 select 訪問狀態資料:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const posts = select('core').getPosts();
const posts = select('core').getPosts();
const posts = select('core').getPosts();

排程操作也是如此:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dispatch('core').savePost(postData);
dispatch('core').savePost(postData);
dispatch('core').savePost(postData);

訂閱狀態更改使用的格式略有不同,但概念相同:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
subscribe(() => {
const newPosts = select('core').getPosts();
// Update your UI based on the new posts
});
subscribe(() => { const newPosts = select('core').getPosts(); // Update your UI based on the new posts });
subscribe(() => {
  const newPosts = select('core').getPosts();
  // Update your UI based on the new posts
});

不過,您並不總是使用預設商店。通常情況下,您需要使用現有的附加商店或註冊自己的商店。

如何註冊WordPress資料儲存

定義您的商店配置並將其註冊到 WordPress 資料包中,首先要匯入 register 函式:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { createReduxStore, register } from '@wordpress/data';
… import { createReduxStore, register } from '@wordpress/data'; …
…
import { createReduxStore, register } from '@wordpress/data';
…

它只需要一個引數–你的商店描述符。接下來,你應該為商店定義一個預設狀態,以設定其預設值:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const DEFAULT_STATE = {
todos: [],
};
… const DEFAULT_STATE = { todos: [], }; …
…
const DEFAULT_STATE = {
  todos: [],
};
…

接下來,建立一個 actions 物件,定義一個用於處理狀態更新的 reducer 函式,並建立一個帶有訪問狀態資料函式的 selectors 物件:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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,
};
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, };
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 物件進行定義。這將初始化商店的操作、選擇器、控制元件和其他屬性:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const store = createReduxStore('my-plugin/todos', {
reducer,
actions,
selectors,
});
const store = createReduxStore('my-plugin/todos', { reducer, actions, selectors, });
const store = createReduxStore('my-plugin/todos', {
  reducer,
  actions,
  selectors,
});

該物件最起碼需要一個還原器來定義狀態的形狀,以及狀態如何隨其他操作而變化。最後,通過呼叫 createReduxStore 定義的儲存描述符來註冊儲存:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
register(store);
register(store);
register(store);

現在,您可以像與其他人一樣與自定義商店進行互動:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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();
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();
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 物件就是一個很好的例子,因為它彙集了所有定義,以便建立用於註冊儲存的描述符:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { createReduxStore } from '@wordpress/data';
const store = createReduxStore( 'demo', {
reducer: ( state = 'OK' ) => state,
selectors: {
getValue: ( state ) => state,
},
} );
import { createReduxStore } from '@wordpress/data'; const store = createReduxStore( 'demo', { reducer: ( state = 'OK' ) => state, selectors: { getValue: ( state ) => state, }, } );
import { createReduxStore } from '@wordpress/data';
  const store = createReduxStore( 'demo', {
    reducer: ( state = 'OK' ) => state,
    selectors: {
    getValue: ( state ) => state,
    },
  } );

這些其他屬性也需要設定和配置。

1. 動作

動作是觸發商店狀態變化的主要方式。它們是簡單的 JavaScript 物件,描述了應該發生的事情。因此,最好先建立這些動作,因為您可以決定要檢索哪些狀態。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const actions = {
addTodo: (text) => ({
type: 'ADD_TODO',
text,
}),
toggleTodo: (index) => ({
type: 'TOGGLE_TODO',
index,
}),
};
const actions = { addTodo: (text) => ({ type: 'ADD_TODO', text, }), toggleTodo: (index) => ({ type: 'TOGGLE_TODO', index, }), };
const actions = {
  addTodo: (text) => ({
    type: 'ADD_TODO',
    text,
  }),
  toggleTodo: (index) => ({
    type: 'TOGGLE_TODO',
    index,
  }),
};

動作建立器採用可選引數,並將返回一個物件,以便傳遞給您定義的還原器:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const actions = {
updateStockPrice: (symbol, newPrice) => {
return {
type: 'UPDATE_STOCK_PRICE',
symbol,
newPrice
};
},
const actions = { updateStockPrice: (symbol, newPrice) => { return { type: 'UPDATE_STOCK_PRICE', symbol, newPrice }; },
const actions = {
  updateStockPrice: (symbol, newPrice) => {
  return {
    type: 'UPDATE_STOCK_PRICE',
    symbol,
    newPrice
  };
},

如果傳入儲存描述符,就可以分派動作建立器並更新狀態值:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dispatch('my-plugin/todos').updateStockPrice('¥', '150.37');
dispatch('my-plugin/todos').updateStockPrice('¥', '150.37');
dispatch('my-plugin/todos').updateStockPrice('¥', '150.37');

將動作物件視為指示還原器如何更改狀態的指令。至少,您可能需要定義建立、更新、讀取和刪除(CRUD)操作。您也可以為動作型別建立一個單獨的 JavaScript 檔案,併為所有這些型別建立一個物件,尤其是當您將它們定義為常量時。

2. 還原器

這裡值得談談還原器,因為它與動作一起扮演著核心角色。它的工作是指定狀態應該如何變化,以響應從動作中得到的指令。如果將動作指令和當前狀態傳遞給它,它就能返回一個新的狀態物件,並將其沿鏈傳遞:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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;
}
};
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; } };
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. 選擇器

為了從註冊儲存訪問當前狀態,你需要選擇器。這是“暴露”商店狀態的主要方式,有助於保持元件與商店內部結構的分離:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const selectors = {
getTodos: (state) => state.todos,
getTodoCount: (state) => state.todos.length,
};
const selectors = { getTodos: (state) => state.todos, getTodoCount: (state) => state.todos.length, };
const selectors = {
  getTodos: (state) => state.todos,
  getTodoCount: (state) => state.todos.length,
};

您可以使用 select 函式呼叫這些選擇器:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const todoCount = select('my-plugin/todos').getTodoCount();
const todoCount = select('my-plugin/todos').getTodoCount();
const todoCount = select('my-plugin/todos').getTodoCount();

然而,選擇器不會將資料傳送到任何地方:它只是顯示資料並提供訪問。

選擇器可以接收儘可能多的引數,以便準確地訪問狀態。它返回的值是這些引數在你定義的選擇器中實現的結果。和操作一樣,你可能會選擇用一個單獨的檔案來儲存所有的選擇器,因為可能會有很多這樣的選擇器。

4. 控制元件

指導網站功能的執行流程或執行其中的邏輯,就是使用控制元件的地方。這些控制元件定義了操作執行流程的行為。將這些控制元件視為 WordPress 資料包中的助手,因為它們是收集狀態傳遞給解析器的中間人。

控制元件還能處理商店中的副作用,例如 API 呼叫或與瀏覽器 API 的互動。通過它們,您可以保持還原器的清潔,同時還能處理複雜的非同步操作:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const controls = {
FETCH_TODOS: async () => {
const response = await fetch('/api/todos');
return response.json();
},
};
const actions = {
fetchTodos: () => ({ type: 'FETCH_TODOS' }),
};
const controls = { FETCH_TODOS: async () => { const response = await fetch('/api/todos'); return response.json(); }, }; const actions = { fetchTodos: () => ({ type: 'FETCH_TODOS' }), };
const controls = {
  FETCH_TODOS: async () => {
    const response = await fetch('/api/todos');
    return response.json();
  },
};

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

這種獲取和返回資料的迴圈對整個流程至關重要。但是,如果沒有動作的呼叫,就無法使用這些資料。

5. 解析器

選擇器會顯示儲存的狀態,但不會明確地將資料傳送到任何地方。解析器與選擇器(和控制元件)連線,以檢索資料。與控制元件一樣,它們也可以處理非同步資料獲取。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const resolvers = {
getTodos: async () => {
const todos = await controls.FETCH_TODOS();
return actions.receiveTodos(todos);
},
};
const resolvers = { getTodos: async () => { const todos = await controls.FETCH_TODOS(); return actions.receiveTodos(todos); }, };
const resolvers = {
  getTodos: async () => {
    const todos = await controls.FETCH_TODOS();
    return actions.receiveTodos(todos);
  },
};

在執行選擇器之前,解析器會確保您所要求的資料在儲存區中可用。解析器和選擇器之間的這種緊密聯絡意味著它們需要匹配名稱。這樣,WordPress 資料包就能根據您請求的資料瞭解要呼叫哪個解析器。

此外,解析器將始終接收您傳入選擇器函式的相同引數,並返回、生成或分派動作物件。

使用WordPress資料包時的錯誤處理

在使用 WordPress 資料包時,您必須執行適當的錯誤處理。如果您選擇處理非同步操作、使用全棧部署或進行 API 呼叫,那麼這一點就更加重要。

例如,如果您分派的操作涉及非同步操作,那麼 try-catch 塊就是一個不錯的選擇:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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 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 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>
  );
};

對於還原器,可以處理錯誤操作並更新狀態:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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 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 reducer = (state = DEFAULT_STATE, action) => {
  switch (action.type) {
    // ... other cases
    case 'FETCH_TODOS_ERROR':
      return {
      ...state,
      error: action.error,
      isLoading: false,
    };
    default:
      return state;
  }
};

在使用選擇器時,您可以加入錯誤檢查功能來處理潛在問題,然後在使用資料前檢查元件中是否存在錯誤:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>
);
};
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> ); };
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 資料包可以幫助您管理狀態。將所有這些整合在一起也是一個實際的考慮因素。讓我們來看一個實時顯示和更新財務資料的股票行情器。

首要任務是為資料建立一個儲存空間:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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);
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);
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 元件來顯示其中的資訊:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>
);
};
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> ); };
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)。本示例使用的是區塊。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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
});
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 });
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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>
);
};
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> ); };
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 元件中設定間隔時間:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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]);
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]);
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 知識

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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'
));
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' ));
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 資料包,還是有其他管理狀態的方法?請在下面的評論中與我們分享您的觀點。

評論留言