loading...
Cover image for Using recursion to build a slideshow with React

Using recursion to build a slideshow with React

chrsgrrtt profile image Chris Garrett ・2 min read

Ever since dabbling with Elixir, I've taken to working pure functional principles into a lot of my everyday coding. In particular, I really enjoy using recursion - I find it especially useful as a way of managing state, especially in combination with React hooks.

One simple way to illustrate how powerful this can be is this Slideshow example - a simple component which cycles through a series of slides, infinitely.

So let's break down some of what's happening in this example, starting with the state management:

const initialIndices = slides.map((_, index) => index);
const stepThroughSlides = async ({ remainingIndices }) => {
  setActiveSlideIndex(remainingIndices[0]);

  await sleep(3000);

  return stepThroughSlides({
    remainingIndices:
      remainingIndices.length > 1
        ? remainingIndices.slice(1)
        : initialIndices
  });
};
stepThroughSlides({ remainingIndices: initialIndices });

There are a few prerequisites to this:

  • We assume a slideshow essentially steps through an array of slides sequentially, by index.
  • We assume that the indices for all of the slides are stored in the initialIndices array.
  • We assume we've used a useState hook to hold the index of the current slide (activeSlideIndex).
  • We assume that the function will receive an array of slides still waiting to be displayed (remainingIndices).

Given all of this is true, on invocation our stepThroughSlides function will:

  1. Update the state to set the first index in the remainingIndices array as the active slide index.
  2. Sleep for 3 seconds.
  3. Call itself again with either:
    • The array of remaining slides indices, minus the one we've just made active.
    • The array of initial slides indices, if we've run out of slides and need to loop back.

With our state changes underway, we just need to update the actual display of our slides:

{slides.map((slide, index) => (
  <div
    className={`slide ${activeSlideIndex === index && "active"}`}
  >
    {slide}
  </div>
))}

Nice and easy - I just map my slides and, if the activeSlideIndex state is equivalent to the index of the slide, I add a CSS class accordingly. Just a few lines of corresponding CSS and we're done:

.slide {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
  transition: opacity 1s ease-in-out;
}

.slide.active {
  opacity: 1;
}

I've glossed over some of the React nuance - like using useEffect to hook this all up - so it's worth having a proper look at the full working example on JSFiddle.


You might be thinking that I could have just used setInterval here - but using recursion:

  • Removes the need for cleanup (ie. clearInterval).
  • Encourages immutability by design - which in turn reduces the risk of bugs and race conditions.
  • As a technique, can be used to much greater effect and applied to more demanding use cases.

Posted on by:

chrsgrrtt profile

Chris Garrett

@chrsgrrtt

Founder, Full Stack Developer & Designer

Discussion

markdown guide