在API请求返回一个值后更新块

时间:2019-11-14 作者:Sean

我有一个块,允许用户在编辑模式下输入脚本,然后将该脚本发送到REST端点,该端点返回WP_Post 表示随后显示给用户的媒体文件。REST端点可能需要几秒钟(甚至几分钟)才能发送回复,因此我希望以异步方式进行更新,并在用户等待时显示微调器。

问题是,即使在REST端点返回其值之后,微调器也会无限期显示。我不确定我做错了什么。以下是我的(缩写)块代码:

( function( wp ) {
    var registerBlockType = wp.blocks.registerBlockType;
    var withSelect = wp.data.withSelect;

    var el = wp.element.createElement;
    var Component = wp.element.Component;
    var Spinner = wp.components.Spinner;

    var __ = wp.i18n.__;

    function getEditComponent( blockName, blockTitle ) {
        return class extends Component {
            render() {
                const { mediaFile, isSelected, className, attributes, setAttributes } = this.props;
                const { script } = attributes;

                // Check if the mediaFile has been populated via REST yet.
                const hasMedia = Array.isArray( mediaFile ) && mediaFile.length;

                if ( isSelected ) {
                    // Show edit textarea.
                    return el(
                        \'div\',
                        { className },
                        el(
                            \'label\',
                            null,
                            __( \'Script:\', \'my-textdomain\' ),
                            el(
                                \'textarea\',
                                {
                                    onChange: ( event ) => {
                                        // Update script attribute as it is typed.
                                        setAttributes( { script: event.target.value } );
                                    },
                                    value: script,
                                    style: {
                                        width: \'100%\'
                                    },
                                    spellCheck: false,
                                    placeholder: __( \'Enter script\', \'my-textdomain\' ),
                                }
                            )
                        )
                    );
                } else {
                    if ( ! hasMedia ) {
                        return (
                            el(
                                Placeholder,
                                {
                                    label: __( \'label\', \'my-textdomain\' ),
                                },
                                // Display spinner until mediaFile has been returned from REST.
                                ! Array.isArray( mediaFile ) ? Spinner() : __( \'Media is being generated.\', \'my-textdomain\' ),
                            )
                        );
                    }

                    return el(
                        \'img\',
                        {
                            src: mediaFile.guid || \'\', // Display mediaFile\'s URL.
                        }
                    );
                }
            }
        };
    };

    const name = \'my-namespace/my-block\';
    const title = __( \'Script\', \'my-textdomain\' );
    const edit = getEditComponent( name, title );

    registerBlockType( name, {
        // Various properties here, like title, description, etc...

        edit: withSelect( ( select, props ) => {
            const { attributes } = props;
            const { script } = attributes;

            // Get media file using script.
            const mediaFile = wp.apiFetch(
                {
                    path: \'/my-api-namespace/v1/do-thing\',
                    method: \'POST\',
                    data: {
                        script: script,
                    },
                }
            );

            return {
                mediaFile,
            };
        } )( edit ),

        save: null, // not shown here
    } );
} )(
    window.wp
);
edit函数是API获取的地方,它被写入edit类的属性中mediaFile. 最初,这是一种Promise 返回人wp.apiFetch, 但最终(我认为)它会成为这种获取的结果(在我的例子中是aWP_Post 阵列)。

编辑类的render 函数检查是否mediaFile 是否填充,并决定是显示“加载”微调器还是显示实际的媒体文件图像。

1 个回复
最合适的回答,由SO网友:Sean 整理而成

我通过阅读React docs. 现在,我使用编辑组件自己的状态来存储从REST检索到的媒体文件,发出REST请求的函数现在位于编辑组件中。我不需要withSelect 再也没有了。

与第一篇文章中的方法相比,这是一种更好的方法,因为它涉及组件设置自己的状态。从组件外部设置组件状态为not allowed, 建议您与组件共享外部范围内的变量,然后更新该变量。此外,传递给组件的属性应该是不可变的。在问题中mediaFile 属性不是不可变的。

这些要点引导我在编辑组件本身中使用其自身的状态实现REST请求。它现在从REST获取媒体文件,并在收到响应时更新其状态。然后,状态由render 函数检查媒体文件是否可用,如果不可用,则显示微调器。这个setState 电话由apiFetch.then 函数强制重新渲染,以便在检索图像时显示图像。

以下是(缩写)工作块:

( function( wp ) {
    var registerBlockType = wp.blocks.registerBlockType;
    var withSelect = wp.data.withSelect;

    var el = wp.element.createElement;
    var Component = wp.element.Component;
    var Spinner = wp.components.Spinner;

    var __ = wp.i18n.__;

    function getEditComponent( blockName, blockTitle ) {
        return class extends Component {
            constructor( props ) {
                super( props );

                this.state = {
                    mediaFile: {},
                };
            }

            getPlot() {
                const { attributes } = this.props;
                const { script } = attributes;

                if ( this.gettingPlot ) {
                    return;
                }

                this.gettingPlot = true;

                wp.apiFetch(
                    {
                        path: \'/my-api-namespace/v1/do-thing\',
                        method: \'POST\',
                        data: {
                            script: script,
                        },
                    }
                ).then(
                    ( media ) => {
                        this.setState(
                            {
                                mediaFile: media,
                            }
                        );

                        this.gettingPlot = false;
                    }
                ).catch(
                    () => {
                        this.gettingPlot = false;
                    }
                );
            }

            render() {
                const { isSelected, className, attributes, setAttributes } = this.props;
                const { script } = attributes;

                let mediaFile = this.state.mediaFile;                
                const hasMedia = ( mediaFile != null && Object.keys( mediaFile ).length );

                if ( isSelected ) {
                    return el(
                        \'div\',
                        { className },
                        el(
                            \'label\',
                            null,
                            __( \'Script:\', \'my-textdomain\' ),
                            el(
                                \'textarea\',
                                {
                                    onChange: ( event ) => {
                                        // Update script attribute as it is typed.
                                        setAttributes( { script: event.target.value } );
                                    },
                                    value: script,
                                    style: {
                                        width: \'100%\'
                                    },
                                    spellCheck: false,
                                    placeholder: __( \'Enter script\', \'my-textdomain\' ),
                                }
                            )
                        )
                    );
                } else {
                    this.getPlot();

                    if ( ! hasMedia ) {
                        return (
                            el(
                                Placeholder,
                                {
                                    label: __( \'label\', \'my-textdomain\' ),
                                },
                                // Display spinner until media file can be read.
                                Spinner(),
                            )
                        );
                    }

                    return el(
                        \'img\',
                        {
                            src: mediaFile.guid || \'\',
                        }
                    );
                }
            }
        };
    };

    const name = \'my-namespace/my-block\';
    const title = __( \'Script\', \'my-textdomain\' );
    const edit = getEditComponent( name, title );

    registerBlockType( name, {
        // Various properties here, like title, description, etc...

        edit,

        save: null, // not shown here
    } );
} )(
    window.wp
);

相关推荐

如何从JavaScript访问自定义POST元数据

我已经为我的WooCommerce产品创建了自定义元字段。然而,我使用的插件(高级Woo搜索)是用JavaScript编写的,我需要访问PHP变量。我用过wp_localize_script() 这样做。然而,在前端,没有显示我的数据。以下是我的PHP代码: function js_enqueue_scripts() { global $post; $text2 = get_post_meta( $post->ID, \'_load_speed_field\',