next fucntion is not called when scrollableTarget was used in antd Modal width react 18
7NZ opened this issue · 12 comments
I use react-infinite-scroll-component in antd's Modal component.
I want to scroll list to the top in some scenario, So I used scrollableTarget
prop and use ref on target div element.
But the next function wasn't called when I scroll list to the bottom.
example: https://codesandbox.io/s/wonderful-orla-58offh?file=/src/index.js
import React from "react";
import ReactDOM from "react-dom/client";
import InfiniteScroll from "react-infinite-scroll-component";
import { Button, Modal } from "antd";
import "antd/dist/antd.css";
const style = {
height: 30,
border: "1px solid green",
margin: 6,
padding: 8
};
function App() {
const [items, setItems] = React.useState(Array.from({ length: 20 }));
const [showDialog, setShowDialog] = React.useState(false);
const openDialog = () => {
setShowDialog(true);
};
const hideDialog = () => {
setShowDialog(false);
};
const fetchMoreData = () => {
// a fake async api call like which sends
// 20 more records in 1.5 secs
setTimeout(() => {
setItems(items.concat(Array.from({ length: 20 })));
}, 1500);
};
return (
<>
<h1>demo: react-infinite-scroll-component</h1>
<hr />
<Button onClick={openDialog}>open dialog</Button>
<Modal
title="infinite-scroll"
open={showDialog}
width="80%"
onCancel={hideDialog}
>
<div id="scrollableDiv" style={{ height: 300, overflow: "auto" }}>
<InfiniteScroll
dataLength={items.length}
next={fetchMoreData}
hasMore={true}
loader={<h4>Loading...</h4>}
scrollableTarget="scrollableDiv"
>
{items.map((i, index) => (
<div style={style} key={index}>
div - #{index}
</div>
))}
</InfiniteScroll>
</div>
</Modal>
</>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
package.json
{
"name": "new",
"version": "1.0.0",
"description": "",
"keywords": [],
"main": "src/index.js",
"dependencies": {
"antd": "4.24.2",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-infinite-scroll-component": "4.1.0",
"react-scripts": "1.1.4"
},
"devDependencies": {},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
Hello,did you solve this problem?
@liulib I use className to querySelector element instead scrollableTarget
. so that the next function can work right.
const scollList = document.querySelector('.js-scroll-list');
if (scollList) {
scollList.scrollTo(0, 0);
}
<InfiniteScroll
className="js-scroll-list"
>
...
</InfiniteScroll>
@7NZ Thanks for the solution 👍
This is not working. Could anyone tell me in brief.
`import React, { useEffect } from "react";
import ReactDOM from "react-dom/client";
import InfiniteScroll from "react-infinite-scroll-component";
import { Button, Modal } from "antd";
import "antd/dist/antd.css";
const style = {
height: 30,
border: "1px solid green",
margin: 6,
padding: 8
};
function App() {
const [items, setItems] = React.useState(Array.from({ length: 20 }));
const [showDialog, setShowDialog] = React.useState(false);
const openDialog = () => {
setShowDialog(true);
};
const hideDialog = () => {
setShowDialog(false);
};
const fetchMoreData = () => {
// a fake async api call like which sends
// 20 more records in 1.5 secs
setTimeout(() => {
setItems(items.concat(Array.from({ length: 20 })));
}, 1500);
};
useEffect(() => {
const scollList = document.querySelector(".js-scroll-list");
if (scollList) {
scollList.scrollTo(0, 0);
}
}, []);
return (
<>
demo: react-infinite-scroll-component
open dialog
<div id="scrollableDiv" style={{ height: 300, overflow: "auto" }}>
<InfiniteScroll
dataLength={items.length}
next={fetchMoreData}
hasMore={true}
loader={
Loading...
}scrollableTarget="scrollableDiv"
className="js-scroll-list"
>
{items.map((i, index) => (
div - #{index}
))}
</>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render();
`
@liulib I use className to querySelector element instead
scrollableTarget
. so that the next function can work right.const scollList = document.querySelector('.js-scroll-list'); if (scollList) { scollList.scrollTo(0, 0); } <InfiniteScroll className="js-scroll-list" > ... </InfiniteScroll>
@vamsimudadla Don't use scrollableTarget
property, use querySelector do whatever you want when dialog opened.
@7NZ
can you please make changes to make in given code sandbox example and please share it over here if possible,
because i am not getting how to implement it.
@7NZ can you please make changes to make in given code sandbox example and please share it over here if possible, because i am not getting how to implement it.
Hello!
use a fixed height
<InfiniteScroll
scrollableTarget="scrollableDiv"
height={200}
Hello,Fixed.
@7NZ can you please make changes to make in given code sandbox example and please share it over here if possible, because i am not getting how to implement it.
Hello! use a fixed height
<InfiniteScroll scrollableTarget="scrollableDiv" height={200}
Thank you very much bro!!!
This works for me.
Give InfiniteScroll a fixed height, and use handleRef function resize height when dialog's height changed.
const [scrollHeight, setScrollHeight] = useState(300);
const handleRef = (node: HTMLDivElement | null) => {
if(node) {
const height = node.scrollHeight
setScrollHeight(height)
}
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="ghost">
<HistoryIcon width={16} className="mr-1" />
History List
</Button>
</DialogTrigger>
<DialogContent className="flex flex-col max-w-4xl h-[90vh]">
<DialogHeader>
<DialogTitle>History List</DialogTitle>
<DialogDescription>
Make changes to your profile here. Click save when you're done.
</DialogDescription>
</DialogHeader>
<div
ref={node => {
handleRef(node)
}}
className="flex-1 overflow-y-auto"
>
<InfiniteScroll
height={scrollHeight}
dataLength={questions.length}
next={fetchMoreData}
hasMore={true}
loader={<SkeletionContent count={2} />}
endMessage={<p className="text-center text-sm text-muted-foreground">All Loaded.</p>}
>
{questions.map((question, index) => ( <div>test</div>))}
</InfiniteScroll>
</div>
</DialogContent>
</Dialog>
)
ScrollableTarget not working with Drawer of ant design. Can someone help me solve this problem?
const NotificationComponent = () => {
const [connectionHub, setConnectionHub] = useState<HubConnection | undefined>(undefined);
const [notifications, setNotifications] = useState<INotification[]>([]);
const [pageIndex, setPageIndex] = useState <number>(1);
const [total, setTotal] = useState <number>(0);
const [hasMore, setHasMore] = useState <boolean>(false);
const [loading, setLoading] = useState(false);
const [open, setOpen] = useState(false);
const [totalUnread, setTotalUnread] = useState <number>(0);
const intl = useIntl();
useEffect(() => {
const connectToHub = async () => {
const user = await getUser();
const hub = new HubConnectionBuilder()
.withUrl(`${GATEWAY}/hub/notification`, {
skipNegotiation: true,
transport: HttpTransportType.WebSockets,
accessTokenFactory: () => user?.access_token ?? '',
})
.build();
setConnectionHub(hub);
hub
.start()
.then(() => {
})
.catch((error) => {
console.error('Error starting SignalR connection:', error);
});
};
const loadData = async () => {
setLoading(true);
const response = await GetNotification({});
if (response.success) {
setNotifications(response.data);
setPageIndex(response.current);
setTotal(response.total);
setTotalUnread(response.dataExtend.totalUnread);
setHasMore(response.dataExtend.hasMore);
}
setLoading(false);
};
loadData()
connectToHub();
}, []);
const onReceiveData = (data: any) => {
notifyUser(data.title, data.message);
setTotal(total + 1);
setTotalUnread(totalUnread + 1);
}
useEffect(() => {
if (connectionHub) {
connectionHub.on('ReceiveNotification', (data: any) => {
setNotifications((prev) => [data,...prev]);
onReceiveData(data)
});
}
}, [connectionHub])
const loadMoreData = async () => {
alert('loading more data');
if (loading) return;
const nextPage = pageIndex + 1;
setLoading(true);
const response = await GetNotification({ current: nextPage });
if (response.success) {
setNotifications((prev) => [...prev,response.data]);
setPageIndex(response.current);
setTotal(response.total);
setTotalUnread(response.dataExtend.totalUnread);
setHasMore(response.dataExtend.hasMore);
}
setLoading(false);
};
return (
<>
<div
style={{
display: 'flex',
height: 26,
}}
onClick={() => setOpen(true)}
>
<Badge count={totalUnread}>
<BellOutlined />
</Badge>
</div>
<Drawer id="scrollableNotification" title={intl.formatMessage({
id: 'component.notification.title',
defaultMessage: 'Notification',
})}
onClose={() => setOpen(false)}
open={open}
style={{overflow: 'auto' }}
>
<InfiniteScroll
dataLength={notifications.length}
next={loadMoreData}
hasMore={ hasMore}
loader={<Skeleton avatar paragraph={{ rows: 1 }} active />}
endMessage={<Divider plain>It is all, nothing more 🤐</Divider>}
scrollableTarget={'scrollableNotification'}
key={'id'}
>
<List
dataSource={notifications}
renderItem={(item) => (
<List.Item key={item.userName} onClick={() => { console.log(item) }} style={{ cursor: 'pointer' }}>
<List.Item.Meta
avatar={<Avatar src={item.icon} />}
title={<span className={item.status === 1 ? 'title-bold' : 'title-default'}>{item.message}</span>}
description={getTimeAgo(new Date())}
key={item.id}
/>
<div>{item.status === 1 ? <Badge className="custom-badge" key={item.status} size={'default'} color={'blue'} /> : <Badge key={item.status} color={''} />}</div>
</List.Item>
)}
/>
</InfiniteScroll>
</Drawer>
</>
);
};
export default NotificationComponent;