在上一部分中,我们创建了一个编辑页面功能,在本部分我们将添加一个创建页面功能。这是我们即将构建的内容预览:

步骤 1:添加“创建新页面”按钮
让我们首先构建一个按钮来显示创建页面表单。它与我们在第 3 部分中构建的编辑按钮类似:
import { useDispatch } from '@wordpress/data';
import { Button, Modal, TextControl } from '@wordpress/components';
function CreatePageButton() {
const [isOpen, setOpen] = useState( false );
const openModal = () => setOpen( true );
const closeModal = () => setOpen( false );
return (
<>
<Button onClick={ openModal } variant="primary">
Create a new Page
</Button>
{ isOpen && (
<Modal onRequestClose={ closeModal } title="Create a new page">
<CreatePageForm
onCancel={ closeModal }
onSaveFinished={ closeModal }
/>
</Modal>
) }
</>
);
}
function CreatePageForm() {
// Empty for now
return <div/>;
}
太棒了!现在让我们让MyFirstApp显示我们闪亮的新按钮:
function MyFirstApp() {
// ...
return (
<div>
<div className="list-controls">
<SearchControl onChange={ setSearchTerm } value={ searchTerm }/>
<CreatePageButton/>
</div>
<PagesList hasResolved={ hasResolved } pages={ pages }/>
</div>
);
}
最终结果应如下所示:

步骤 2:提取受控的 PageForm
现在按钮已经就位,我们可以完全专注于构建表单。本教程是关于管理数据的,所以我们不会构建一个完整的页面编辑器。相反,表单将只包含一个字段:帖子标题。
幸运的是,我们在第三部分中构建的EditPageForm已经让我们走了 80% 的路。大部分用户界面已经可用,我们将在CreatePageForm中重用它们。让我们首先将表单 UI 提取到单独的组件中:
function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
// ...
return (
<PageForm
title={ page.title }
onChangeTitle={ handleChange }
hasEdits={ hasEdits }
lastError={ lastError }
isSaving={ isSaving }
onCancel={ onCancel }
onSave={ handleSave }
/>
);
}
function PageForm( { title, onChangeTitle, hasEdits, lastError, isSaving, onCancel, onSave } ) {
return (
<div className="my-gutenberg-form">
<TextControl
label="Page title:"
value={ title }
onChange={ onChangeTitle }
/>
{ lastError ? (
<div className="form-error">Error: { lastError.message }</div>
) : (
false
) }
<div className="form-buttons">
<Button
onClick={ onSave }
variant="primary"
disabled={ !hasEdits || isSaving }
>
{ isSaving ? (
<>
<Spinner/>
Saving
</>
) : 'Save' }
</Button>
<Button
onClick={ onCancel }
variant="tertiary"
disabled={ isSaving }
>
Cancel
</Button>
</div>
</div>
);
}
这种代码质量更改不应改变应用程序的工作方式。让我们尝试编辑一个页面以确保:

太好了!编辑表单仍然存在,现在我们有了构建块来支持新的CreatePageForm。
步骤 3:构建 CreatePageForm
CreatePageForm组件必须做的唯一事情是提供渲染PageForm组件所需的以下七个属性:
- title
- onChangeTitle
- hasEdits
- lastError
- isSaving
- onCancel
- onSave
让我们看看如何实现:
Title, onChangeTitle, hasEdits
EditPageForm更新并保存了 Redux 状态中存在的现有实体记录。因此,我们依赖于editedEntityRecords选择器。
在CreatePageForm的情况下,没有预存的实体记录。只有一个空表单。用户输入的任何内容都是该表单本地的,这意味着我们可以使用 React 的useState钩子来跟踪它:
function CreatePageForm( { onCancel, onSaveFinished } ) {
const [title, setTitle] = useState();
const handleChange = ( title ) => setTitle( title );
return (
<PageForm
title={ title }
onChangeTitle={ setTitle }
hasEdits={ !!title }
{ /* ... */ }
/>
);
}
onSave, onCancel
在EditPageForm中,我们分发了saveEditedEntityRecord('postType', 'page', pageId )操作来保存 Redux 状态中的编辑。
然而,在CreatePageForm中,我们在 Redux 状态中没有任何编辑,也没有pageId。在这种情况下我们需要分发的操作称为saveEntityRecord(名称中没有“Edited”一词),它接受一个代表新实体记录的对象,而不是pageId。
传递给saveEntityRecord的数据通过 POST 请求发送到相应的 REST API 端点。例如,分发以下操作:
saveEntityRecord( 'postType', 'page', { title: "Test" } );
会触发一个 POST 请求到/wp/v2/pages WordPress REST API端点,请求体中只有一个字段:title=Test。
现在我们对saveEntityRecord了解更多了,让我们在CreatePageForm中使用它。
function CreatePageForm( { onSaveFinished, onCancel } ) {
// ...
const { saveEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
const savedRecord = await saveEntityRecord(
'postType',
'page',
{ title }
);
if ( savedRecord ) {
onSaveFinished();
}
};
return (
<PageForm
{ /* ... */ }
onSave={ handleSave }
onCancel={ onCancel }
/>
);
}
还有一个细节需要解决:我们新创建的页面尚未被PagesList拾取。根据 REST API 文档,/wp/v2/pages端点默认以status=draft创建(POST 请求)页面,但返回(GET 请求)的是status=publish的页面。解决方案是显式传递status参数:
function CreatePageForm( { onSaveFinished, onCancel } ) {
// ...
const { saveEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
const savedRecord = await saveEntityRecord(
'postType',
'page',
{ title, status: 'publish' }
);
if ( savedRecord ) {
onSaveFinished();
}
};
return (
<PageForm
{ /* ... */ }
onSave={ handleSave }
onCancel={ onCancel }
/>
);
}
请继续将该更改应用于您的本地CreatePageForm组件,然后处理剩余的两个属性。
lastError, isSaving
EditPageForm通过getLastEntitySaveError和isSavingEntityRecord选择器获取错误和进度信息。在这两种情况下,它都传递了以下三个参数:( 'postType', 'page', pageId )。
然而,在CreatePageForm中,我们没有pageId。那怎么办?我们可以跳过pageId参数来获取关于没有 id 的实体记录的信息——这将是新创建的记录。useSelect调用与EditPageForm中的非常相似:
function CreatePageForm( { onCancel, onSaveFinished } ) {
// ...
const { lastError, isSaving } = useSelect(
( select ) => ( {
// 注意缺少 pageId 参数:
lastError: select( coreDataStore )
.getLastEntitySaveError( 'postType', 'page' ),
// 注意缺少 pageId 参数
isSaving: select( coreDataStore )
.isSavingEntityRecord( 'postType', 'page' ),
} ),
[]
);
// ...
return (
<PageForm
{ /* ... */ }
lastError={ lastError }
isSaving={ isSaving }
/>
);
}
就是这样!以下是我们的新表单的实际效果:


将所有内容连接起来
以下是我们在本章中构建的所有内容:
function CreatePageForm( { onCancel, onSaveFinished } ) {
const [title, setTitle] = useState();
const { lastError, isSaving } = useSelect(
( select ) => ( {
lastError: select( coreDataStore )
.getLastEntitySaveError( 'postType', 'page' ),
isSaving: select( coreDataStore )
.isSavingEntityRecord( 'postType', 'page' ),
} ),
[]
);
const { saveEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
const savedRecord = await saveEntityRecord(
'postType',
'page',
{ title, status: 'publish' }
);
if ( savedRecord ) {
onSaveFinished();
}
};
return (
<PageForm
title={ title }
onChangeTitle={ setTitle }
hasEdits={ !!title }
onSave={ handleSave }
lastError={ lastError }
onCancel={ onCancel }
isSaving={ isSaving }
/>
);
}
function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
const { page, lastError, isSaving, hasEdits } = useSelect(
( select ) => ( {
page: select( coreDataStore ).getEditedEntityRecord( 'postType', 'page', pageId ),
lastError: select( coreDataStore ).getLastEntitySaveError( 'postType', 'page', pageId ),
isSaving: select( coreDataStore ).isSavingEntityRecord( 'postType', 'page', pageId ),
hasEdits: select( coreDataStore ).hasEditsForEntityRecord( 'postType', 'page', pageId ),
} ),
[pageId]
);
const { saveEditedEntityRecord, editEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
const savedRecord = await saveEditedEntityRecord( 'postType', 'page', pageId );
if ( savedRecord ) {
onSaveFinished();
}
};
const handleChange = ( title ) => editEntityRecord( 'postType', 'page', page.id, { title } );
return (
<PageForm
title={ page.title }
onChangeTitle={ handleChange }
hasEdits={ hasEdits }
lastError={ lastError }
isSaving={ isSaving }
onCancel={ onCancel }
onSave={ handleSave }
/>
);
}
function PageForm( { title, onChangeTitle, hasEdits, lastError, isSaving, onCancel, onSave } ) {
return (
<div className="my-gutenberg-form">
<TextControl
label="页面标题:"
value={ title }
onChange={ onChangeTitle }
/>
{ lastError ? (
<div className="form-error">错误:{ lastError.message }</div>
) : (
false
) }
<div className="form-buttons">
<Button
onClick={ onSave }
variant="primary"
disabled={ !hasEdits || isSaving }
>
{ isSaving ? (
<>
<Spinner/>
保存中
</>
) : '保存' }
</Button>
<Button
onClick={ onCancel }
variant="tertiary"
disabled={ isSaving }
>
取消
</Button>
</div>
</div>
);
}
剩下的就是刷新页面并享受表单:
