使用 AutoSizer 包装 React-Beautiful-DND + React-Window 虚拟列表遇到的设置问题

Setup problem encountered by wrapping a React-Beautiful-DND + React-Window virtual list with AutoSizer

包装我的虚拟列表时 <FixedSizeList> 组件 (react-beautiful-dnd + react-window ) 使用 <AutoSizer> 组件 (react-virtualized-auto-sizer) 我收到以下错误:

react-beautiful-dnd

A setup problem was encountered.

Invariant failed: provided.innerRef has not been provided with a HTMLElement.

如果我不使用 <AutoResizer> 包装 <FixedSizeList> 组件并改为提供硬编码值,则不会发生错误。

我的程序实现了 2 个独立的、不可拖动的列表,我可以在它们之间拖放。因为列表不可拖动,所以它不是典型的 "board",但我使用 React-Beautiful-DND's CodeSandBox for React-Window Basic Board 作为指南,使其同样有效。

List.js:

import { Draggable, Droppable } from "react-beautiful-dnd";
import { FixedSizeList } from "react-window";
import AutoSizer from "react-virtualized-auto-sizer";
import ListItem from "./ListItem";
import React, { memo, useCallback } from "react";

const List = ({

        ID,
        data
    }) => {

    const listItemRenderer = useCallback(({ data, index, style }) => {

        const item = (data && data[index]);

        if (!item) {

            return null;
        }

        return (

            <Draggable
                key={item.ID}
                draggableId={item.ID}
                index={index}
            >
                {(provided) =>

                    <ListItem
                        data={item}
                        draggableProvided={provided}
                        virtualStyle={style}
                    />
                }
            </Draggable>
        );
    }, []);

    return (

        <Droppable
            droppableId={ID}
            mode={"virtual"}
            renderClone={(provided, snapshot, rubric) => (

                <ListItem
                    data={data[rubric.source.index]}
                    draggableProvided={provided}
                    isDragging={snapshot.isDragging}
                />
            )}
        >
            {(provided, snapshot) => {

                const dataLength = (data)
                    ? data.length
                    : 0;

                const itemCount = (snapshot.isUsingPlaceholder)
                    ? dataLength + 1
                    : dataLength;

                return (

                    <AutoSizer>  //Error here caused by wrapping <FixedSizeList> with <AutoSizer>
                        {({ width, height }) => (

                            <FixedSizeList
                                width={width}   //AutoSizer supplied value
                                height={height} //AutoSizer supplied value
                                itemSize={100}
                                itemCount={itemCount}
                                itemData={data}
                                outerRef={provided.innerRef}
                            >
                                {listItemRenderer}
                            </FixedSizeList>
                        )}
                    </AutoSizer>
                );
            }}
        </Droppable>
    );
};

export default memo(List);

ListItem.js:

import React, { memo } from "react";

const ListItem = ({

        data,
        draggableProvided,
        virtualStyle
    }) => {

    return (

        <div
            {...draggableProvided.draggableProps}
            {...draggableProvided.dragHandleProps}
            ref={draggableProvided.innerRef}
            style={{

                ...draggableProvided.draggableProps.style,
                ...virtualStyle
            }}

        >
            {data.name}
        </div>
    );
};

export default memo(ListItem);

无论出现什么错误,一切似乎仍能正常运行,但我真的很想在继续之前了解问题所在。

我深入 AutoSizer 组件寻找答案。

记录错误是因为 AutoSizer HOC 的 children 属性 由于 widthheight 值为 0 而未呈现。这也是为什么一切仍然正常运行的原因,因为 widthheight 状态值最终被更新,但仅在初始渲染之后。

AutoSizer (index.esm.js):

// Avoid rendering children before the initial measurements have been collected.
// At best this would just be wasting cycles.
var bailoutOnChildren = false;

if (!disableHeight) {
if (height === 0) {
    bailoutOnChildren = true;
}
outerStyle.height = 0;
childParams.height = height;
}

if (!disableWidth) {
if (width === 0) {
    bailoutOnChildren = true;
}
outerStyle.width = 0;
childParams.width = width;
}

return createElement(
'div',
{
    className: className,
    ref: this._setRef,
    style: _extends({}, outerStyle, style) },
!bailoutOnChildren && children(childParams)
);

因此,解决方案是为 defaultWidthdefaultHeight props 提供非零值,以确保组件呈现在安装,尽管具有非自动尺寸:

//...

return (

    <AutoSizer
        defaultWidth={1}
        defaultHeight={1}
    >
        {({ width, height }) => (

            <FixedSizeList
                width={width} 
                height={height}e
                itemSize={100}
                itemCount={itemCount}
                itemData={data}
                outerRef={provided.innerRef}
            >
                {listItemRenderer}
            </FixedSizeList>
        )}
    </AutoSizer>
);