使用 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
属性 由于 width
和 height
值为 0
而未呈现。这也是为什么一切仍然正常运行的原因,因为 width
和 height
状态值最终被更新,但仅在初始渲染之后。
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)
);
因此,解决方案是为 defaultWidth
和 defaultHeight
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>
);
包装我的虚拟列表时 <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
属性 由于 width
和 height
值为 0
而未呈现。这也是为什么一切仍然正常运行的原因,因为 width
和 height
状态值最终被更新,但仅在初始渲染之后。
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)
);
因此,解决方案是为 defaultWidth
和 defaultHeight
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>
);