Build an Infinite Scroll in React using React Hooks

The infinite list (scroll) feature has existed for a while. It’s a great way to enhance your application’s UX.

There are many third-party libraries available, but if you like to challenge yourself as I do, we’ll explore the implementation from scratch using Hooks in this post.

Let’s quick code a simple app component. We initialize an empty state and pass references to the state and setState as props to our InfiniteList component.

export default function App() {
  
  const [state, setState] = useState([]);
return (
    <div className='App'>
      <InfiniteList 
        state={state}
        setState={setState}
      />
    </div>
  );
};

The InfiniteList component will return an unordered list of dog images fetched from the Dog API.

Once our request is successful, we push the images to the parent’s state. We’ll make sure not to overwrite the previous state, as we’ll use the getData() function multiple times to fetch more images. So, dot dot dot . . .

function InfiniteList(props) {
useEffect(() => {
    getData();
  }, []);
const getData = () => {
    fetch('https://dog.ceo/api/breeds/image/random/15')
      .then(res => {
        return !res.ok 
        ? res.json().then(e => Promise.reject(e))
        : return res.json()
      }
      .then(res => props.setState([...props.state, ...res.message]))
      .catch(console.log);
}
return (
    <ul id='list'>
      { props.state.map((img, i) => (
          <li key={i} style={{backgroundImage: `url(${img})`}}/>)}
    </ul>
  );

So far, this displays the first 15 images.

Now we need to listen for an event when a user scrolls down to the very bottom of the viewport, and fetches more images, calling getData() again.

But how do we determine the bottom reach? I hope this image will help you to understand.

  1. window.innerHeight<strong> </strong>— static, the height of the user’s browser window.
  2. window.scrollY — dynamic property, current scroll position.
  3. list.clientHeight — static, the height of the container (ul element).
  4. list.offsetTop — container’s indent (if any) from the top of the page, static.

Okay, if 1+2===3+4, we know that a user is at the bottom of the page and we need to fetch.

But what if we want the ul to be a fixed height? We need to get the ul’s properties:

  1. element.scrollHeight — static value, the total scrollable height.
  2. element.scrollTop** **— dynamic value, the current scroll top position.
  3. element.clientHeight — static value, element’s current height (excluding overflow).

Hence, 1===2+3 will indicate that the user has reached the bottom of the container. And now we can conditionally tell the component whether it’s of fixed height, by setting the scrollable property.

Let’s put this all together in the new useEffect hook.

There’s one last fix to make this all work. Let’s initialize a state, then, once the condition above is met, we toggle the state, giving it a signal to fetch more, by making our first hook dependent on the state’s value.

const [loadMore, setLoadMore] = useState(false);useEffect(() => {
  setLoadMore(false);
  getData();
}, [loadMore]);

We can also conditionally render a loading spinner at the bottom while waiting for the API response, by initializing a new state with some truthy value, and then resetting it once the request is fulfilled.

Also, to avoid potential bugs, we could remove the event listener from the window object when the component unmounts, by adding a return statement in useEffect like so:

useEffect(() => {
  widnow.addEventListener('event', function);
  return () => {
    window.removeEventListener('event', function);
  }
}, [])

#reactjs #web-development

Build an Infinite Scroll in React using React Hooks
151.80 GEEK