WordPress

błędy – Walidacja bloku nie powiodła się dla bloku komponentu React, który rehydratuje się po wejściu w życie

  • 3 września, 2024
  • 4 min read
błędy – Walidacja bloku nie powiodła się dla bloku komponentu React, który rehydratuje się po wejściu w życie


Mam ten konkretny blok

niż gdy wstawiam do Edytora, pojawia mi się ten błąd:

wpisz tutaj opis obrazka

a błąd brzmi następująco:

Treść wygenerowana przez save funkcjonować:


Treść pobrana z treści posta:

"mój-blok"/
"mój-blok"/

Kod komponentu React:

import { HydratedInnerBlock } from '../../../src/components/HydratedInnerBlock';
import { DEFAULT_IMAGE } from '../../../src/constants';
import { useStyleModeClassName } from '../../../src/hooks/useStyleModeClassName';
import { cssClassName } from '../../../src/util/cssClassName';

export const MyComponent = ( props ) => {
    const { mediaSrc, isImage, styleMode, isCardLeft, innerBlockProps } = props;
    const classNames = useStyleModeClassName( styleMode );
    const [ baseClassName ] = classNames;

    return (
        
{ isImage && ( "mój-blok" ) } { ! isImage && (
); };

Kod edytora bloku:

import { useEffect } from '@wordpress/element';
import {
    useBlockProps,
    InspectorControls,
    MediaUpload,
    useInnerBlocksProps,
} from '@wordpress/block-editor';
import { Panel, PanelBody, ToggleControl } from '@wordpress/components';

import { DEFAULT_IMAGE } from '../../src/constants';
import { EditorFileSelector } from '../../src/components/EditorFileSelector';

import './Editor.scss';
import { cssClassName } from '../../src/util/cssClassName';
import { useStyleModeClassName } from '../../src/hooks/useStyleModeClassName';
import { ColorModeSelector } from '../../src/components/ColorModeSelector';

/**
 * The edit function describes the structure of your block in the context of the
 * editor. This represents what the editor will render when the block is used.
 * Note this is a client-side component, so you can use client-side functionality
 * (hooks, events, ajax calls, etc)
 */

const TEMPLATE = [
    [
        'core/heading',
        {
            content:
                'Content 1',
            level: 2,
            className: 'my-block__title',
        },
    ],
    [
        'core/heading',
        {
            content: 'Content2',
            level: 5,
            className: 'my-block__subtitle',
        },
    ],
    [
        'core/paragraph',
        {
            content:
                'Content3',
            className: 'my-block__description',
        },
    ],
    [
        'core/buttons',
        {},
        [
            [
                'core/button',
                {
                    text: 'Get Started',
                    className: 'btn btn-dark',
                },
            ],
        ],
    ],
];
export const Editor = ( {
    attributes: {
        styleMode,
        mediaSrc,
        ctaTarget,
        isImage,
        isCardLeft,
        previewImage,
    },
    setAttributes,
} ) => {
    const blockProps = useBlockProps();
    const classNames = useStyleModeClassName( styleMode );
    const [ baseClassName, invertedClassName ] = classNames;
    const innerBlockProps = useInnerBlocksProps( undefined, {
        template: TEMPLATE,
    } );

    useEffect( () => {
        const button = document.querySelector( '.btn' );
        if ( button ) {
            button.className="";
            button.classList.add( 'btn', `btn-${ invertedClassName }` );
        }
    }, [ invertedClassName ] );

    const handleMediaUpload = ( value ) => {
        setAttributes( { mediaSrc: value.url } );
    };

    const handleLinkTab = ( value ) => {
        setAttributes( { ctaTarget: value } );
    };

    const handleMediaTypeChange = ( value ) => {
        setAttributes( { isImage: value } );
    };

    const handleCardPosition = ( value ) => {
        setAttributes( { isCardLeft: value } );
    };

    const handleSelectStyleMode = ( value ) => {
        setAttributes( { styleMode: value } );
    };

    if ( previewImage?.length > 0 ) {
        return (
            "Zapowiedź"
        );
    }

    return (
        
{ isImage && ( "mój-blok" ) } { ! isImage && (
); };

I jego Save.js:

/**
 * The 'Save' component of the block. This renders on server-side only
 * We separate this initializer from the 'Form' component because
 * the 'Form' component would also be used after hydration.
 *
 */
export const Save = ( { attributes } ) => {
    const blockProps = useBlockProps.save();
    const innerBlockProps = useInnerBlocksProps.save();
    const { isDarkMode } = attributes;

    const blockState = JSON.stringify( {
        ...attributes,
    } )
        // remove html comments
        .replace( /<\!--.+?-->/g, '' );

    return (
        <>
            
`, } } /> > ); };

The Save.js has that structure because I rehydrate the block after selecting custom attributes (theme dark/light) from custom metaboxes from the Editor's panel:

/**
 * Initializes the client-side component.
 * Hydrates the server-side rendered component and initializes client-side functionality.
 *
 */
const MyBlock = (props) => {
  const { styleMode } = props;
  const classNames = useStyleModeClassName(styleMode);
  const [baseClassName, invertedClassName] = classNames;

  useEffect(() => {
    const button = document.querySelector(".btn");
    if (button) {
      button.className = "";
      button.classList.add("btn", `btn-${invertedClassName}`);
    }
  }, [baseClassName, invertedClassName]);

  return ;
};

/**
 * If we have any blocks hydrate the server-side renderered HTML
 */
hydrate(
  ".my-block",
  MyBlock,
  true
);

Oto mój hydrate funkcja, jeśli to pomoże:

import { createRoot } from '@wordpress/element';

/**
 * Helper function to hydrate any custom block following our approach for hydration
 *
 * @param {string}   selector         indicates the identifier for the container of the block to hydrate
 * @param {Function} Component        indicates the react component to hydrate with
 * @param {boolean}  isUseInnerBlocks indicates the react component uses inner blocks, to hydrate them recursively
 */
export function hydrate( selector, Component, isUseInnerBlocks ) {
    // mutation observer
    const mutationObserverConfig = {
        attributes: true,
        childList: true,
        subtree: isUseInnerBlocks ? true : false,
    };

    const initializeBlocks = () => {
        const elements = document.querySelectorAll( selector );

        if ( elements.length ) {
            elements.forEach( ( element ) => {
                if ( element.attributes[ 'data-hydrated' ]?.value === '1' ) {
                    // already hydrated, ignore
                    return;
                }

                // read hydration data
                const closestParent = element.closest( '[data-props]' );

                if ( ! closestParent ) {
                    // no props, nothing to hydrate
                    return;
                }

                let attributeValue =
                    closestParent.attributes[ 'data-props' ].value;

                if ( attributeValue === 'prev-sibling' ) {
                    const indexOf = Array.prototype.indexOf.call(
                        closestParent.parentNode.childNodes,
                        closestParent
                    );

                    attributeValue =
                        closestParent.parentNode.childNodes[ indexOf - 1 ]
                            .childNodes[ 0 ].textContent;
                }

                const data = JSON.parse( attributeValue );
                const innerBlockPropsStr = isUseInnerBlocks
                    ? closestParent
                            .querySelector( '[data-innerblocks]' )
                            ?.innerHTML?.replace( /data-hydrated="1"/gi, '' )
                    : undefined;

                // render new instance replacing the existing element (hydrate)
                const root = createRoot( element );

                root.render(
                    
                );

                element.setAttribute( 'data-hydrated', '1' );
            } );
        }
    };

    // eslint-disable-next-line no-undef
    const observer = new MutationObserver( initializeBlocks );

    // Start observing the target node for configured mutations
    observer.observe( document.body, mutationObserverConfig );
}

Zmieniłem strukturę Save.js, naprawiając błąd WordPressa, ale to psuje hydrate funkcja ta powoduje zachowanie domyślnego motywu (ciemnego) i brak aktualizacji kolorów po przejściu na tryb jasny.

Dziękuję z góry za pomoc!


Źródło

Warto przeczytać!  php — Odmiany produktów Woocommerce są tworzone bez wybranych wartości atrybutów