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

构建创建页面表单

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

剩下的就是刷新页面并享受表单:

构建创建页面表单

接下来做什么?