最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Re-rendering a custom embed block on attribute change

programmeradmin3浏览0评论

I am attempting to refactor a block plugin that provides a custom embed from a third-party platform. I would like the generated embed to re-render in the editor when the user changes attributes. I had this working fine a few years ago, before the editor was an iframe. I am having some difficulty achieving the same result now.

My edit function looks like this:

export default function Edit({ attributes, setAttributes }) {

    const blockProps = useBlockProps({
        className: 'altmetric-embed'
    });

    const onChangeIdentifier = (value) => {
        setAttributes({ identifier: value });
    };

    useEffect(() => {

        _altmetric_embed_init( );

    }, [attributes.identifier]);

    return (
        <>
            <InspectorControls>
                    <PanelBody title={__('Identifier')} >
                        <PanelRow>
                            <TextControl
                                label='DOI'
                                value={attributes.identifier}
                                onChange={onChangeIdentifier}
                            />
                        </PanelRow>
                    </PanelBody>
            </InspectorControls>
            
            <div
                    { ...blockProps}
                    data-doi={attributes.identifier}
            ></>
    


        </>
    );
}

The _altmetric_embed_init function is provided by the third-party in the instance when you want to refresh the embed after the DOM is loaded. (Information on that is here: .html#technical-information.) I've registered their script in PHP in the standard way:

wp_register_script(
    'altmetrics-official-embed',
    '.js',
    '',
    '',
    true
);

And enqueued it via the block.json file:

{
    "$schema": ".json",
    "apiVersion": 3,
    "example": {},
    "supports": {
        "html": false
    },
    "attributes": {
        "identifier": {
            "type": "string"
        },
    "editorScript": "file:./index.js",
    "editorStyle": "file:./editor.css",
    "style": "file:./style.css",
    "script": "altmetrics-official-embed"
}

As far as I can tell the script is enqueued correctly and useEffect is firing correctly, but the embed doesn't re-render. I assume the iframe is tripping this up somewhere? I believe they are using defaultView correctly (/), but their code is of course minimized. Should I be using useRefEffect here? Any pointers on the best way to do that?

I am attempting to refactor a block plugin that provides a custom embed from a third-party platform. I would like the generated embed to re-render in the editor when the user changes attributes. I had this working fine a few years ago, before the editor was an iframe. I am having some difficulty achieving the same result now.

My edit function looks like this:

export default function Edit({ attributes, setAttributes }) {

    const blockProps = useBlockProps({
        className: 'altmetric-embed'
    });

    const onChangeIdentifier = (value) => {
        setAttributes({ identifier: value });
    };

    useEffect(() => {

        _altmetric_embed_init( );

    }, [attributes.identifier]);

    return (
        <>
            <InspectorControls>
                    <PanelBody title={__('Identifier')} >
                        <PanelRow>
                            <TextControl
                                label='DOI'
                                value={attributes.identifier}
                                onChange={onChangeIdentifier}
                            />
                        </PanelRow>
                    </PanelBody>
            </InspectorControls>
            
            <div
                    { ...blockProps}
                    data-doi={attributes.identifier}
            ></>
    


        </>
    );
}

The _altmetric_embed_init function is provided by the third-party in the instance when you want to refresh the embed after the DOM is loaded. (Information on that is here: https://badge-docs.altmetric/getting-started.html#technical-information.) I've registered their script in PHP in the standard way:

wp_register_script(
    'altmetrics-official-embed',
    'https://d1bxh8uas1mnw7.cloudfront/assets/embed.js',
    '',
    '',
    true
);

And enqueued it via the block.json file:

{
    "$schema": "https://schemas.wp/trunk/block.json",
    "apiVersion": 3,
    "example": {},
    "supports": {
        "html": false
    },
    "attributes": {
        "identifier": {
            "type": "string"
        },
    "editorScript": "file:./index.js",
    "editorStyle": "file:./editor.css",
    "style": "file:./style.css",
    "script": "altmetrics-official-embed"
}

As far as I can tell the script is enqueued correctly and useEffect is firing correctly, but the embed doesn't re-render. I assume the iframe is tripping this up somewhere? I believe they are using defaultView correctly (https://make.wordpress/core/2021/06/29/blocks-in-an-iframed-template-editor/), but their code is of course minimized. Should I be using useRefEffect here? Any pointers on the best way to do that?

Share Improve this question asked Mar 18 at 15:18 jshwlkrjshwlkr 5341 gold badge6 silver badges24 bronze badges 4
  • are you sure that it's going to load your registered script? script is for the frontend, editorScript is for the backend. Also keep in mind that the div with the block props might have the classname the script is looking for but it's not a HTML tag, it's a react component. The DOM element the script finds and attaches to may be destroyed and recreated at any moment and altmetrics might not be able to handle that. It might be easier to use the second method on the altmetrics docs and useRef but that's moving outside the realm of WordPress and into general react programming – Tom J Nowell Commented Mar 18 at 18:38
  • as for iframes, it's unlikely but also easy to test for, check your browsers error console for security errors. You might also want to debounce the identifier so it doesn't recreate the badge on every key press – Tom J Nowell Commented Mar 18 at 18:39
  • My understanding is that script enqueues for both front-end and editor: developer.wordpress/block-editor/reference-guides/block-api/… – jshwlkr Commented Mar 18 at 18:47
  • I'll note I'm seeing no console errors. – jshwlkr Commented Mar 18 at 18:50
Add a comment  | 

2 Answers 2

Reset to default 0

I would like the generated embed to re-render in the editor when the user changes attributes.

You can do that by creating a ref using useRef and then passing the ref to _altmetric_embed_init, like so:

const ref = useRef();

useEffect(() => {
    _altmetric_embed_init(ref.current);
}, [attributes.identifier]);

You don't have to use useRefEffect and you also don't need to load altmetrics-official-embed in the editor canvas (which is an iframe).

  • In my block.json file, I used "editorScript": [ "file:./index.js", "altmetrics-official-embed" ] and removed the script entry.

However, instead of assigning the altmetric-embed class and data-doi attribute to your block wrapper, you should assign them to an element in that wrapper, like so:

<div {...useBlockProps({ ref })}>
    <div
        className="altmetric-embed"
        data-doi={attributes.identifier}
    >
    </div>
</div>

Use useRef to get a reference to the container and call _altmetric_embed_init() on the DOM node or its context.

import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, PanelRow, TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useEffect, useRef } from '@wordpress/element';

export default function Edit({ attributes, setAttributes }) {
    const blockProps = useBlockProps({
        className: 'altmetric-embed',
    });

    const { identifier } = attributes;

    const embedRef = useRef();

    const onChangeIdentifier = (value) => {
        setAttributes({ identifier: value });
    };

    useEffect(() => {
        // Make sure the script is available and the embed container is mounted
        if (typeof _altmetric_embed_init === 'function' && embedRef.current) {
            _altmetric_embed_init();
        }
    }, [identifier]);

    return (
        <>
            <InspectorControls>
                <PanelBody title={__('Identifier')}>
                    <PanelRow>
                        <TextControl
                            label="DOI"
                            value={identifier}
                            onChange={onChangeIdentifier}
                        />
                    </PanelRow>
                </PanelBody>
            </InspectorControls>

            <div
                {...blockProps}
                data-doi={identifier}
                ref={embedRef}
            ></div>
        </>
    );
}
发布评论

评论列表(0)

  1. 暂无评论