Material UI Autocomplete + Infinite Scrolling together?

7.7k Views Asked by At

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" />
      )}
    />
2

There are 2 best solutions below

0
On

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.

0
On
    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