构建页面列表

在这一部分中,我们将构建一个所有WordPress页面的可过滤列表。这是本节末尾的应用程序外观:

让我们看看如何逐步实现目标。

步骤1:构建PagesList组件

首先,我们构建一个最小的React组件来显示页面列表:

函数MyFirstApp(){const页面=[{id:“模拟”,标题:“示例页面”}]return<PagesList pages={pages}/>;}函数PagesList({pages}){返回(<ul>{pages?.map(第=>页(<li key={page.id}>{页面标题}</li>) ) }</ul>);}

注意,这个组件还没有获取任何数据,只显示了页面的硬编码列表。刷新页面时,应看到以下内容:

步骤2:获取数据

硬编码的示例页不是很有用。我们想显示您的实际WordPress页面,所以让我们从WordPress REST API.

在开始之前,让我们确认我们确实有一些页面要获取。在WPAdmin中,使用侧栏菜单导航到页面,并确保它至少显示四个或五个页面:

如果没有,继续创建几个页面——你可以使用与上面截图中相同的标题。确保出版不仅如此节约他们。

现在我们有了要处理的数据,让我们深入研究代码。我们将利用@文字新闻/核心数据这个包提供了解析器、选择器和操作来使用WordPress核心API。@文字新闻/核心数据构建在@文字新闻/数据包裹。

要获取页面列表,我们将使用获取实体记录选择器。概括地说,它将发出正确的API请求,缓存结果,并返回我们需要的记录列表。以下是使用方法:

wp.data.select('core').getEntityRecords('postType','page')

如果您在浏览器的开发工具中运行以下代码片段,您将看到它返回无效的.为什么?这些页面仅由获取实体记录首次运行选择器。如果您稍等片刻,然后重新运行它,它将返回所有页面的列表。

注意:要直接运行此类命令,请确保浏览器显示的是块编辑器的实例(任何页面都可以)。否则选择(“核心”)函数将不可用,您将得到一个错误。

类似地我的第一个应用程序一旦数据可用,组件需要重新运行选择器。这正是使用Select钩子可以:

从“@wordpress/data”导入{useSelect};从“@wordpress/core-data”导入{store as coreDataStore};函数MyFirstApp(){const pages=使用选择(选择=>select(coreDataStore).getEntityRecords('postType','page'),[]);// ...}函数PagesList({pages}){// ...<li键={page.id}>{page.title.rentered}</li>// ...}

请注意,我们使用进口index.js中的语句。这使插件能够使用wp_排队_脚本.对以下内容的任何引用核心数据存储编译为相同的wp.数据我们在浏览器的开发工具中使用的参考。

使用Select接受两个参数:回调和依赖项。大致来说,每当依赖项或底层数据存储发生变化时,它都会重新运行回调。你可以了解更多使用Select在中数据模块文档.

综合起来,我们得到以下代码:

从“@wordpress/data”导入{useSelect};从“@wordpress/core-data”导入{store as coreDataStore};从“@wordpress/html-entities”导入{decodeEntities};函数MyFirstApp(){const页面=使用选择(选择=>select(coreDataStore).getEntityRecords('postType','page'),[]);return<PagesList pages={pages}/>;}函数PagesList({pages}){返回(<ul>{pages?.map(第=>页(<li键={page.id}>{decodeEntities(page.title.rendered)}</li>) ) }</ul>)}

注意,文章标题可能包含如下HTML实体&aacute;,所以我们需要使用decodeEntities(解码实体)函数将其替换为它们所代表的符号á.

刷新页面时应显示与此类似的列表:

第三步:把它变成一张桌子

函数PagesList({pages}){返回(<table className=“wp-list-table widefat fixed striped table-view-list”><头部><tr>标题</tr></thead><t车身>{pages?.map(第=>页(<tr密钥={page.id}><td>{decodeEntities(page.title.rendered)}</td></tr>) ) }</tbody></表格>);}

现在页面列表很短;然而,它长得越长,就越难使用。WordPress管理员通常用搜索框解决这个问题——让我们也实现一个!

让我们从添加搜索字段开始:

从'react'导入{useState};从“@wordpress/components”导入{SearchControl};函数MyFirstApp(){const[searchTerm,setSearchTerm]=使用状态('');// ...返回(<div><搜索控制onChange={setSearchTerm}value={searchTerm}/>{/* ... */ }</div>)}

请注意,不要使用输入标签,我们利用了搜索控制组件。它看起来是这样的:

字段开始为空,内容存储在搜索术语状态值。如果您不熟悉使用状态胡克,你可以在React的文档.

我们现在只能请求与搜索术语.

在使用WordPress API文档,我们看到/wp/v2/页端点接受搜索查询参数并将其用于将结果限制为与字符串匹配的结果.但我们如何使用它?我们可以将自定义查询参数作为第三个参数传递给获取实体记录如下:

wp.data.select('core').getEntityRecords('postType','page',{search:'home'})

在浏览器的开发工具中运行该代码段将触发一个请求/wp/v2/pages?搜索=主页而不仅仅是/wp/v2/页.

让我们在我们的使用Select调用如下:

从“@wordpress/data”导入{useSelect};从“@wordpress/core-data”导入{store as coreDataStore};函数MyFirstApp(){// ...const{pages}=useSelect(select=>{常量查询={};如果(searchTerm){query.search=搜索术语;}返回{页面:select(coreDataStore).getEntityRecords('postType','page',query)}},[searchTerm]);// ...}

这个搜索术语现在用作搜索提供时查询参数。请注意搜索术语也在列表中指定使用Select依赖关系以确保获取实体记录搜索术语变化。

最后,以下是如何我的第一个应用程序一旦我们把它连接在一起:

从'react'导入{useState};从“react-dom”导入{createRoot};从“@wordpress/components”导入{SearchControl};从“@wordpress/data”导入{useSelect};从“@wordpress/core-data”导入{store as coreDataStore};函数MyFirstApp(){const[searchTerm,setSearchTerm]=使用状态(“”);const pages=useSelect(选择=>{常量查询={};if(searchTerm){query.search=搜索术语;}return select(coreDataStore).getEntityRecords('postType','page',query);},[searchTerm]);返回(<div><搜索控制onChange={setSearchTerm}值={searchTerm}/><PagesList pages={pages}/></div>)}

瞧!我们现在可以筛选结果:

使用核心数据代替直接调用API

让我们暂停片刻,考虑一下我们本可以采用的另一种方法的缺点——直接使用API。假设我们直接发送了API请求:

从“@wordpress/api-fetch”导入apiFetch;函数MyFirstApp(){// ...const[pages,setPages]=使用状态([]);使用效果(()=>{consturl='/wp-json/wp/v2/pages?search='+搜索术语;apiFetch({url}).then(设置页面)},[searchTerm]);// ...}

在核心数据之外工作,我们需要解决两个问题。

首先,无序更新。搜索“关于”将触发五个API请求筛选A类,抗体,阿布,阿布,以及关于。这些请求的完成顺序可能与开始顺序不同。有可能搜索=A将在_search=About_之后解析,因此我们将显示错误的数据。

古腾堡数据有助于处理幕后的异步部分。使用Select记住最近的调用并只返回我们期望的数据。

其次,每次击键都会触发一个API请求。如果你键入关于,删除它,然后重新键入,它将总共发出10个请求,即使我们可以重用数据。

Gutenberg数据有助于缓存由获取实体记录()并在后续调用中重用它们。当其他组件依赖于相同的实体记录时,这一点尤为重要。

总之,核心数据中内置的实用程序是为了解决典型问题而设计的,这样您就可以专注于您的应用程序。

步骤5:装载指示器

我们的搜索功能有一个问题。我们无法确定它是否仍在搜索或没有显示结果:

一些信息如正在加载…没有结果会把它清理干净。让我们实施它们!第一,页面列表必须了解当前状态:

从“@wordpress/components”导入{SearchControl,Spinner};函数PagesList({hasResolved,pages}){if(!已解决){return<微调器/>}if(!页?.长度){return<div>无结果</div>}// ...}函数MyFirstApp(){// ...返回(<div>// ...<PagesList hasResolved={hasResorved}pages={pages}/></div>)}

注意,我们没有构建自定义加载指示器,而是利用了微调器组件。

我们仍然需要知道页面选择器是否已解决或者没有。我们可以使用已完成解决选择器:

wp.data.select('core').hasFinishedResolution('getEntityRecords',['postType','page',{search:'home'}])

它接受选择器的名称和与传递给该选择器的参数完全相同并返回其中之一真实的如果数据已经加载或如果我们还在等。让我们将其添加到使用Select:

从“@wordpress/data”导入{useSelect};从“@wordpress/core-data”导入{store as coreDataStore};函数MyFirstApp(){// ...const{pages,hasResolved}=useSelect(select=>{// ...返回{页面:select(coreDataStore).getEntityRecords('postType','page',query),已解决:select(coreDataStore).hasFinishedResolution('getEntityRecords',['postType','page',query]),}},[searchTerm]);// ...}

只有最后一个问题。很容易输入错误并最终向传递不同的参数获取实体记录已完成解决。至关重要的是,它们是相同的。我们可以通过将参数存储在变量中来消除此风险:

从“@wordpress/data”导入{useSelect};从“@wordpress/core-data”导入{store as coreDataStore};函数MyFirstApp(){// ...const{pages,hasResolved}=useSelect(select=>{// ...const selectorArgs=['postType','page',query];返回{页面:select(coreDataStore).getEntityRecords(…selectorArgs),已解决:select(coreDataStore).hasFinishedResolution('getEntityRecords',selectorArgs),}},[searchTerm]);// ...}

瞧!就是这样。

将其连接在一起

所有部件都就位了,太棒了!以下是我们应用程序的完整JavaScript代码:

从'react'导入{useState};从“react-dom”导入{createRoot};从“@wordpress/components”导入{SearchControl,Spinner};从“@wordpress/data”导入{useSelect};从“@wordpress/core-data”导入{store as coreDataStore};从“@wordpress/html-entities”导入{decodeEntities};函数MyFirstApp(){const[searchTerm,setSearchTerm]=使用状态(“”);const{pages,hasResolved}=useSelect((选择)=>{常量查询={};if(searchTerm){query.search=搜索术语;}const selectorArgs=['postType','page',query];返回{页面:选择(coreDataStore).getEntityRecords(…选择器参数),hasResolved:选择(coreDataStore).hasFinishedResolution('获取实体记录',选择器参数),};},[搜索术语]);返回(<div><SearchControl onChange={setSearchTerm}value={searchTerm}/><PagesList hasResolved={hasResorved}pages={pages}/></div>);}函数PagesList({hasResolved,pages}){if(!已解决){return<微调器/>;}if(!页?长度){return无结果;}返回(<table className=“wp-list-table widefat fixed striped table-view-list”><头部><tr><td>标题</td></tr></thead><t车身>{pages?.map((page)=>(<tr键={page.id}><td>{decodeEntities(page.title.rendered)}</td></tr>) ) }</tbody></表格>);}const根=创建根(document.querySelector('#my first gutenberg app'));window.addEventListener(“加载”,函数(){根.根.根(<MyFirstApp/>);},);

剩下的就是刷新页面并享受全新的状态指示器:


接下来是什么?