问题:react-infinite-scroll-component组件在Drawer/Modal中,局部滚动的应用,按照官方示例使用scrollableTarget绑定id会存在无法下拉滚动的问题;
import { render } from "react-dom";
import React, { useCallback, useState } from "react";
import InfiniteScroll from "react-infinite-scroll-component";
import { Drawer } from "antd";
const InfiniteScrollDrawer = ({ open, setOpen }) => {
const [items, setItems] = useState(Array.from({ length: 30 }));
const listItems = items.map((_, index) => <div key={index}>{index}</div>);
const [listItemsContainerRef, setListItemsContainerRef] = useState();
const onlistItemsContainerRefChange = useCallback((node) => {
if (node !== null) {
setListItemsContainerRef(node);
}
}, []);
return (
<Drawer
width={"60%"}
destroyOnClose={true} //关闭时销毁子元素,避免重新打开数据不会刷新
open={open}
onClose={() => setOpen(false)}
title={"demo"}
>
<div style={{ height: "100px" }}>非滚动区域</div>
<div
style={{
height: "300px",
// height: 'calc(100vh - 100px)', // this works also
display: "flex",
flexDirection: "column",
background: "lightyellow"
}}
>
<div style={{ padding: "20px", background: "lightblue" }}>Header</div>
<div id={"scrollableDiv"} style={{ overflow: "auto" }}>
<InfiniteScroll
dataLength={listItems.length}
next={() => {
window.setTimeout(() => {
setItems((prevItems) => [
...prevItems,
...Array.from({ length: 30 })
]);
}, 1500);
}}
hasMore={true}
loader={<p style={{ color: "red" }}>...loading</p>}
scrollableTarget={"scrollableDiv"}
>
{listItems}
</InfiniteScroll>
</div>
</div>
</Drawer>
);
};
const App = () => {
const [open, setOpen] = useState(false); // faking modal show
return (
<div>
<button onClick={() => setOpen(true)}>show</button>
<button onClick={() => setOpen(false)}>hide</button>
<InfiniteScrollDrawer open={open} setOpen={setOpen} />
</div>
);
};
render(<App />, document.getElementById("root"));
解决方案:由于Drawer和Modal的渲染机制问题,需要确保能够成功绑定InfiniteScroll的滚动容器的scrollableTarget,因此采用ref实时获取滚动容器,并确保在ref有值时绑定nfiniteScroll;
codesandbox代码成功示例
import { render } from "react-dom";
import React, { useCallback, useState } from "react";
import InfiniteScroll from "react-infinite-scroll-component";
import { Drawer } from "antd";
const InfiniteScrollDrawer = ({ open, setOpen }) => {
const [items, setItems] = useState(Array.from({ length: 30 }));
const listItems = items.map((_, index) => <div key={index}>{index}</div>);
const [listItemsContainerRef, setListItemsContainerRef] = useState();
const onlistItemsContainerRefChange = useCallback((node) => {
if (node !== null) {
setListItemsContainerRef(node);
}
}, []);
return (
<Drawer
width={"60%"}
destroyOnClose={true} //关闭时销毁子元素,避免重新打开数据不会刷新
open={open}
onClose={() => setOpen(false)}
title={""}
>
<div style={{ height: "100px" }}>非滚动区域</div>
<div
style={{
height: "300px",
// height: 'calc(100vh - 100px)', // this works also
display: "flex",
flexDirection: "column",
background: "lightyellow"
}}
>
<div style={{ padding: "20px", background: "lightblue" }}>Header</div>
<div ref={onlistItemsContainerRefChange} style={{ overflow: "auto" }}>
{listItemsContainerRef && (
<InfiniteScroll
dataLength={listItems.length}
next={() => {
window.setTimeout(() => {
setItems((prevItems) => [
...prevItems,
...Array.from({ length: 30 })
]);
}, 1500);
}}
hasMore={true}
loader={<p style={{ color: "red" }}>...loading</p>}
scrollableTarget={listItemsContainerRef}
>
{listItems}
</InfiniteScroll>
)}
</div>
</div>
</Drawer>
);
};
const App = () => {
const [open, setOpen] = useState(false); // faking modal show
return (
<div>
<button onClick={() => setOpen(true)}>show</button>
<button onClick={() => setOpen(false)}>hide</button>
<InfiniteScrollDrawer open={open} setOpen={setOpen} />
</div>
);
};
render(<App />, document.getElementById("root"));