admin管理员组

文章数量:1122846

I'm building a scrollable horizontal gallery block (Please, spare me the comments about scrollable horizontal galleries being bad from a UI perspective). The gallery may have a text at the start or at the end. The width of this text box can be resized visually using a handle. This is the code I have now (some code omitted for brevity):

const [currentWidth, setCurrentWidth] = useState(textWidth);

useEffect(() => {
    setCurrentWidth(textWidth);
}, [textWidth]);

const handleResizeStop = (event, direction, elt, delta) => {
    const newWidth = parseInt(currentWidth) + delta.width;
    setCurrentWidth(`${newWidth}px`);
    setAttributes({ textWidth: `${newWidth}px` });
};

const setTextWidth = (value) => {
    setAttributes({ textWidth: value })
}

const setTextPosition = (value) => {
    setAttributes({ textPosition: textPosition === value ? undefined : value });
};


<div {...blockProps}>
    <div className="exhibit-items" style={{ overflowX: 'scroll', display: 'flex' }}>
        {textPosition === 'start' &&
            <ResizableBox
                size={{ width: currentWidth }}
                minWidth="100px"
                maxWidth="500px"
                lockAspectRatio={false}
                enable={{ top: false, right: true, bottom: false, left: false }}
                onResizeStop={handleResizeStop}
            >
                <div className="exhibit-item" style={{ maxWidth: textWidth, width: '100%' }}>
                    <InnerBlocks
                        allowedBlocks={ALLOWED_BLOCKS}
                        template={TEMPLATE}
                    />
                </div>
            </ResizableBox>
        }
        {displayItems(items)}
        {textPosition === 'end' &&
            <ResizableBox
                size={{ width: currentWidth }}
                minWidth="100px"
                maxWidth="500px"
                lockAspectRatio={false}
                enable={{ top: false, right: false, bottom: false, left: true }}
                onResizeStop={handleResizeStop}
            >
                <div className="exhibit-item" style={{ maxWidth: textWidth, width: '100%' }}>
                    <InnerBlocks
                        allowedBlocks={ALLOWED_BLOCKS}
                        template={TEMPLATE}
                    />
                </div>
            </ResizableBox>
        }
    </div>
</div>

When the text box is at the start of the gallery, the resize handle works as expected. However, when it's at the end, dragging the handle immediately scrolls me back to the start of the gallery, causing the text box and the resize handle to go out of view. Does React scroll back automatically when there's a re-render? If so, how can I avoid this?

I'm building a scrollable horizontal gallery block (Please, spare me the comments about scrollable horizontal galleries being bad from a UI perspective). The gallery may have a text at the start or at the end. The width of this text box can be resized visually using a handle. This is the code I have now (some code omitted for brevity):

const [currentWidth, setCurrentWidth] = useState(textWidth);

useEffect(() => {
    setCurrentWidth(textWidth);
}, [textWidth]);

const handleResizeStop = (event, direction, elt, delta) => {
    const newWidth = parseInt(currentWidth) + delta.width;
    setCurrentWidth(`${newWidth}px`);
    setAttributes({ textWidth: `${newWidth}px` });
};

const setTextWidth = (value) => {
    setAttributes({ textWidth: value })
}

const setTextPosition = (value) => {
    setAttributes({ textPosition: textPosition === value ? undefined : value });
};


<div {...blockProps}>
    <div className="exhibit-items" style={{ overflowX: 'scroll', display: 'flex' }}>
        {textPosition === 'start' &&
            <ResizableBox
                size={{ width: currentWidth }}
                minWidth="100px"
                maxWidth="500px"
                lockAspectRatio={false}
                enable={{ top: false, right: true, bottom: false, left: false }}
                onResizeStop={handleResizeStop}
            >
                <div className="exhibit-item" style={{ maxWidth: textWidth, width: '100%' }}>
                    <InnerBlocks
                        allowedBlocks={ALLOWED_BLOCKS}
                        template={TEMPLATE}
                    />
                </div>
            </ResizableBox>
        }
        {displayItems(items)}
        {textPosition === 'end' &&
            <ResizableBox
                size={{ width: currentWidth }}
                minWidth="100px"
                maxWidth="500px"
                lockAspectRatio={false}
                enable={{ top: false, right: false, bottom: false, left: true }}
                onResizeStop={handleResizeStop}
            >
                <div className="exhibit-item" style={{ maxWidth: textWidth, width: '100%' }}>
                    <InnerBlocks
                        allowedBlocks={ALLOWED_BLOCKS}
                        template={TEMPLATE}
                    />
                </div>
            </ResizableBox>
        }
    </div>
</div>

When the text box is at the start of the gallery, the resize handle works as expected. However, when it's at the end, dragging the handle immediately scrolls me back to the start of the gallery, causing the text box and the resize handle to go out of view. Does React scroll back automatically when there's a re-render? If so, how can I avoid this?

Share Improve this question asked Jul 22, 2024 at 16:11 leemonleemon 2,0024 gold badges22 silver badges51 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Apparently, this is the standard behavior of the library on which the ResizableBox component is based:

https://github.com/bokuweb/re-resizable/issues/727

To fix my issue I had to add the following code to my block:

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

// Refs used to retain block's scroll position while dragging the resize handle.
const parentScrollOffsetX = useRef(0)
const parentScrollOffsetY = useRef(0)

// Store the current scroll position when resizing starts.
const handleResizeStart = (event, direction, ref) => {
    parentScrollOffsetX.current = ref.parentElement?.scrollLeft || 0
    parentScrollOffsetY.current = ref.parentElement?.scrollTop || 0
};

// Maintain the current scroll position while resizing.
const handleResize = (event, direction, ref, delta) => {
    ref.parentElement.scrollTo(parentScrollOffsetX.current, parentScrollOffsetY.current)
};

...

<ResizableBox
    size={{ width: currentWidth }}
    minWidth="250px"
    maxWidth="500px"
    lockAspectRatio={false}
    enable={{ top: false, right: false, bottom: false, left: true }}
    onResizeStart={handleResizeStart}
    onResize={handleResize}
    onResizeStop={handleResizeStop}
>
    <div className="exhibit-item">
        <InnerBlocks
            allowedBlocks={ALLOWED_BLOCKS}
            template={TEMPLATE}
        />
    </div>
</ResizableBox>

本文标签: reactAvoid automatic scroll jump when dragging a resize handle in a Gutenberg block