react-infinite-scroll-component组件在Drawer/Modal中无法下拉滚动加载的解决办法

西门梓
2023-12-01

问题:react-infinite-scroll-component组件在Drawer/Modal中,局部滚动的应用,按照官方示例使用scrollableTarget绑定id会存在无法下拉滚动的问题;

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={"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"));

解决方案参考:react-infinite-scroll-component官方Issue

 类似资料: