Повторная отрисовка пользовательского блока встраивания при изменении атрибута.

Вопрос или проблема

Я пытаюсь рефакторить плагин блока, который предоставляет пользовательский встроенный код от сторонней платформы. Я хотел бы, чтобы созданный встроенный код обновлялся в редакторе, когда пользователь изменяет атрибуты. Ранее у меня это работало хорошо, несколько лет назад, до того как редактор стал использовать iframe. Сейчас у меня возникают трудности с достижением такого же результата.

Моя функция редактирования выглядит следующим образом:

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={__('Идентификатор')} >
                        <PanelRow>
                            <TextControl
                                label="DOI"
                                value={attributes.identifier}
                                onChange={onChangeIdentifier}
                            />
                        </PanelRow>
                    </PanelBody>
            </InspectorControls>
            
            <div
                    { ...blockProps}
                    data-doi={attributes.identifier}
            ></>
    


        </>
    );
}

Функция _altmetric_embed_init предоставлена сторонней платформой и используется в случае, когда вы хотите обновить встроенный код после загрузки DOM. (Информация об этом здесь: https://badge-docs.altmetric.com/getting-started.html#technical-information.) Я зарегистрировал их скрипт в PHP стандартным способом:

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

И подключил его через файл block.json:

{
    "$schema": "https://schemas.wp.org/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"
}

Насколько я могу судить, скрипт подключен правильно и useEffect выполняется корректно, но встроенный код не обновляется. Я предполагаю, что iframe как-то мешает этому процессу? Я верю, что они используют defaultView правильно (https://make.wordpress.org/core/2021/06/29/blocks-in-an-iframed-template-editor/), но их код, конечно, минимизирован. Должен ли я использовать useRefEffect здесь? Есть ли какие-то советы по лучшему способу сделать это?

Ответ или решение

Для решения проблемы повторного рендеринга кастомного блока для встраивания при изменении атрибутов, необходимо учитывать несколько ключевых аспектов. В данном случае, мы имеем дело с интеграцией стороннего элемента через плагин блоков в редакторе Gutenberg, который теперь использует iframe. Этот переход на использование iframe может вызывать сложности, связанные с взаимодействием между контекстами документа и окна.

Теория

1. Встраивание и JavaScript в WordPress

Когда мы встраиваем сторонние скрипты в WordPress, особенно в контексте редактора Gutenberg, важно обеспечивать, что они корректно взаимодействуют с DOM, который теперь находится внутри iframe. Это означает, что любые изменения, которые необходимо применить к документу, должны адекватно отражаться именно в этом изолированном environment.

2. Проблематика iframe

Iframe создают отдельный контекст для содержания, что влияет на доступ к DOM из глобального контекста. Функции JavaScript, которые запрашивают или манипулируют элементами DOM, должны понимать, в каком контексте они работают. Многие сторонние библиотеки, если они не оптимизированы для использования внутри iframe, могут неправильно обрабатывать такие сценарии.

Пример

У вас есть функция Edit, которая получает атрибуты и обновляет их:

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}></div>
        </>
    );
}

Функция _altmetric_embed_init() вызывается каждый раз, когда изменяется атрибут identifier. Однако она, вероятно, ожидает, что вызов будет произведен в контексте, соответствующем встраиваемому окну.

Применение

1. Использование useRef или useRefEffect

Важным аспектом может быть применение useRef или более специализированного useRefEffect, если последний предоставляет особенности для работы с контекстом iframe. Рассмотрим возможность использования useRef для захвата и управления DOM элементом:

const embedRef = useRef(null);

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

2. Проверка загрузки скрипта

Убедитесь, что скрипт действительно загружен и становится активным в нужный момент. Для этого можно использовать событие загрузки страницы или проверку на наличие необходимых функций из библиотеки.

3. Проверка содержания

Иногда скрипты могут ожидать конкретной структуры или даже физического существования элементов на момент их инициализации. Это может потребовать пересмотра вызовов функций и проверки готовности элементов.

4. Использование методов из API для работы с iframe

Если _altmetric_embed_init() не поддерживает кастомные контексты, возможно стоит использовать технологии, которые позволяют отправлять сообщения между главным окном и iframe, такие как postMessage.

Результирующий код может выглядеть так:

useEffect(() => {
    const embedDocument = embedRef.current?.contentWindow?.document;
    if (embedDocument) {
        embedDocument.dispatchEvent(new Event('altmetricEmbedInit'));
    }
}, [attributes.identifier]);

Эти изменения могут помочь в интеграции, если ваше приложение ожидает инициализации контекста документально или неспособно корректно инициализироваться в пределах iframe без явных сигналов.

Проверка обновлений библиотеки

Проверьте, может библиотека была обновлена для поддержки работы внутри iframe самостоятельно или предоставляет новые API для этого случая.

Таким образом, подход в устранении проблемы заключается в корректном управлении инициализацией встраиваемых элементов и гарантии того, что они взаимодействуют с корректным контекстом DOM. Это может потребовать как манипуляций с кодом, так и изменений в последовательности загрузки и обработки JavaScript.

Оцените материал
Добавить комментарий

Капча загружается...