gutenberg icon indicating copy to clipboard operation
gutenberg copied to clipboard

The page jumps to top back when you scroll down, if paragraph block is selected in the custom child block (using innerBlock)

Open dvaansiseelr opened this issue 2 months ago • 1 comments

I'm not sure if it is a bug or not. But need a little help to solve the behavior issue.

I’ve developed two custom blocks — a parent block and a child block. When a user clicks a paragraph block inside the custom child block and scrolls down until the selected block is out of view, then the page jumps to top back.

To see this behavior in action, please open the video link:

How to prevent this automatic jumping behavior?

/* parent block */

import { backgroundColor, foregroundColor } from '../shared/settings';

const { registerBlockType } = wp.blocks;
const { __ } = wp.i18n;
const { useBlockProps, InnerBlocks  } = wp.blockEditor;
const { createBlock } = wp.blocks;
const { Button } = wp.components;
const { dispatch, useSelect } = wp.data;
const blockName = "spa/child-block";

registerBlockType( 'spa/parent-block', {
    title: 'SPA parent',
    apiVersion: 3,
    category: 'edhotels',
    attributes: {
        heading: {
            type: "string"
        }
    },
    allowedBlocks: [ blockName ],
    icon: {
        background: backgroundColor,
        foreground: foregroundColor,
        src: 'align-full-width',
    },

    edit: (props) => {
        const { clientId } = props;

        const { blockCount } = useSelect(select => {
            return {
                blockCount: select('core/block-editor').getBlockOrder(clientId).length
            }
        });

        const addBlock = () => {
            const addBlock = createBlock(blockName);
            dispatch('core/block-editor').insertBlocks(addBlock, blockCount, clientId);
        }
        
        return (

            <div { ...useBlockProps(
                {
                    className: 'parent-block'
                }
            ) }>
                <div className="add-block-action">
                    <Button
                        isPrimary
                        onClick = { addBlock }
                        className="add-block-button"
                        >
                        { __('Add a block', 'edhotels') }
                    </Button>
                </div>
                
                <InnerBlocks
                    template = {[[blockName]]}
                    // className = "parent-block"
                    allowedBlocks={ [blockName] }
                    templateLock={ false }
                    renderAppender={ false }
                    orientation = "horizontal"
                />
            </div>
        );
    },
    save: (props) => {
            const { attributes } = props;
            const { } = attributes;
            
            return (
                <div {...useBlockProps.save(
                    { 
                        className: 'parent-block',
                     }
                )}>
                    <InnerBlocks.Content />
                </div>
            );
        }
});

/* child block */

import { backgroundColor, foregroundColor } from '../shared/settings';

const { registerBlockType, createBlocksFromInnerBlocksTemplate } = wp.blocks;
const { __ } = wp.i18n;
const {
    useBlockProps,
    useInnerBlocksProps,
    InnerBlocks,
    __experimentalBlockVariationPicker: BlockVariationPicker,
    store: blockEditorStore,
} = wp.blockEditor;
const { Path, SVG } = wp.components;
const { dispatch, useSelect } = wp.data;

const variations = [
    {
        name: "one",
        title: __("One"),
        description: __("One"),
        icon: (
            <SVG
                xmlns="http://www.w3.org/2000/svg"
                width="48"
                height="48"
                viewBox="0 0 48 48"
            >
                <Path d="M0 10a2 2 0 0 1 2-2h44a2 2 0 0 1 2 2v28a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V10Z" />
            </SVG>
        ),
        // isDefault: true,
        innerBlocks: [
            ["core/paragraph", {content: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."}]
        ],
        scope: ["inserter"],
    },
    {
        name: "two",
        title: __("Two"),
        description: __("Two"),
        icon: (
            <SVG
                xmlns="http://www.w3.org/2000/svg"
                width="48"
                height="48"
                viewBox="0 0 48 48"
            >
                <Path d="M0 10a2 2 0 0 1 2-2h44a2 2 0 0 1 2 2v28a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V10Z" />
            </SVG>
        ),
        // isDefault: true,
        innerBlocks: [
            ["core/paragraph", {content: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."}],
            ["core/paragraph", {content: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."}]
        ],
        scope: ["inserter"],
    },
    {
        name: "three",
        title: __("Three"),
        description: __("Three"),
        icon: (
            <SVG
                xmlns="http://www.w3.org/2000/svg"
                width="48"
                height="48"
                viewBox="0 0 48 48"
            >
                <Path d="M0 10a2 2 0 0 1 2-2h44a2 2 0 0 1 2 2v28a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V10Z" />
            </SVG>
        ),
        // isDefault: true,
        innerBlocks: [
            ["core/paragraph", {content: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."}],
            ["core/paragraph", {content: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."}],
            ["core/paragraph", {content: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."}]

        ],
        scope: ["inserter"],
    }
];

registerBlockType('spa/child-block', {
    title: 'SPA child',
    apiVersion: 3,
    category: 'edhotels',
    parent: ['spa/parent-block'],
    icon: {
        background: backgroundColor,
        foreground: foregroundColor,
        src: 'align-full-width',
    },
    supports: {
        html: true,
        inserter: false,
        reusable: false,
    },
    allowedBlocks: [ 'core/paragraph' ],

    edit: (props) => {

        const blockProps = useBlockProps( );
        const { setAttributes, clientId } = props;

        function EditContainer() {

            return <section {...useBlockProps()}>
                <InnerBlocks
                    template = {[['core/paragraph']]}
                    className = "block-slider-inner"
                    templateLock={ false }
                    renderAppender={ false }
                    orientation = "horizontal"
                />
            </section>
        }

        const innerBlocks = useSelect(
            (select) => select(blockEditorStore).getBlocks(clientId),
            [clientId]
        );
        
        const hasInnerBlocks = innerBlocks.length > 0;
        if (!hasInnerBlocks) {
            return (
                <div {...blockProps}>

                    <BlockVariationPicker
                        label="Section Variant"
                        variations={variations}
                        onSelect={(variation = variations[0]) => {
                            
                            const { replaceInnerBlocks } = dispatch('core/block-editor');
                            
                            if (variation.attributes) {
                                setAttributes(variation.attributes);
                            }
                            if (variation.innerBlocks) {
                                replaceInnerBlocks(
                                    clientId,
                                    createBlocksFromInnerBlocksTemplate(
                                        variation.innerBlocks
                                    ),
                                    false
                                );
                            }

                        }}
                        // allowSkip
                    />

                </div>
            );
        }
        
        return (
            <div {...blockProps}>
                
                <EditContainer />

            </div>
        );
    },

    save: () => {
        const blockProps = useBlockProps.save( );
        const innerBlocksProps = useInnerBlocksProps.save( blockProps );
        return <div { ...innerBlocksProps } />;
    }
});

dvaansiseelr avatar Oct 18 '25 10:10 dvaansiseelr

I see some issues in your code that I think are worth fixing, then letting us know if the issue still happens:

  • The first (parent) block has a useSelect without a dependency array.
  • For props like template and allowedBlocks a new array reference is passed on every render, you can define these as variables outside of your edit component (e.g. near where blockName is defined).
  • The EditContainer function component is defined inline within another function component, which is an anti-pattern to avoid. This component will have a new reference on every render, so React will be unable to track it across renders as the same component that's being rendered, which will cause it to re-mount on every render. I don't think you need EditContainer to be a component, you can inline the code. Or extract it from function.

My guess is the last one is the cause of the bug.

talldan avatar Oct 20 '25 02:10 talldan