Gauri Padbidri
Gauri Padbidri

Reputation: 411

Material UI Autocomplete + Infinite Scrolling together?

Problem : Getting double Scrollbars - Removing Paper Scrollbar makes the autocomplete content not scrollable hence showing ONLY the contents in the visible height of the dropdown. If I hide the other scroll then the Infinite Scroll API does not get invoked. How can I get it working :

Description -

I am trying to create a Infinite Scroll with Material UI Autocomplete for which I am using react-infinite-scroll-component attached link for reference

The way I implemented is :

As we need to attach the Infinite Scroll to the Popper that renders the list items; hence I have written my custom PAPER Component (as per documentation it is responsible for rendering items in the dropdown ) PaperComponent={myCustomComponent}

My InfiniteScrollAutoComplete definition is attached below :

<Autocomplete
      options={list.data && list.data !== null ? list.data : []}
      getOptionLabel={(option) => option.name}
      PaperComponent={(param) => (
        <InfiniteScroll
          height={200}
          dataLength={list.total}
          next={this.handleFetchNext.bind(this)}
          hasMore={list.data.length < list.total ? true : false}
          loader={
            <p style={{ textAlign: "center", backgroundColor: "#f9dc01" }}>
              <b>Loading...</b>
            </p>
          }
          endMessage={
            <p style={{ textAlign: "center", backgroundColor: "#f9dc01" }}>
              <b>Yay! You have seen it all</b>
            </p>
          }
        >
          <Paper {...param}  />
        </InfiniteScroll>
      )}
      renderInput={(params) => (
        <TextField {...params} label="" variant="outlined" />
      )}
    />

Upvotes: 5

Views: 9406

Answers (2)

Stef Leurs
Stef Leurs

Reputation: 21

You need to implement your own list in the PaperComponent and ignore the param object:

PaperComponent={
  () => {
    return <Paper id={paperId}>
      <InfiniteScroll
        dataLength={data.length}
        next={this.loadNext.bind(this)}
        hasMore={hasMorePages}
        loader={<h4 style={{ textAlign: 'center' }}>Loading ...</h4>}
        height={400}
      >
        <List>
          {data && data.map(value => <ListItem disablePadding>
            <ListItemButton onMouseDown={(event) => this.handleSelected(event, value)}>
              <ListItemText primary={value.name} />
            </ListItemButton>
          </ListItem>
          )}
          {data && data.length === 0 && <ListItem disablePadding>
            <ListItemButton disabled>
              <ListItemText primary="No items available" />
            </ListItemButton>
          </ListItem>}
        </List>
      </InfiniteScroll>
    </Paper>
  }
}

Be sure to use onMouseDown event handler instead of onClick, otherwise selecting an item won't work.

Upvotes: 0

iam_ruk
iam_ruk

Reputation: 1

    const observer = useRef();
     
    const lastOptionElementRef = useCallback((node) => {
        if (observer.current) observer.current.disconnect();
            observer.current = new IntersectionObserver(async (entries) => {
                if (entries[0].isIntersecting && props.hasMore) {
                    setPageNumber((pageNumber) => pageNumber + 1);
                }
            });
        if (node) observer.current.observe(node);
    }, [props.loader]);

you can add this lastOptionElementRef to the last element of the options using the render option prop. This will trigger an function whenever the last option is visible in the viewport. Also, it avoids the scrolling issue

Upvotes: 0

Related Questions