DEV Community

loading...

Create a Slideshow With React

AnxinYang
A front-end developer.
・2 min read

Let's create a simple slideshow component today. It's easy and only take few steps to achieve.
Here is a demo and source code.

In order to create a slideshow, we need to have two components:

  • The container that holds contents.
  • The individual slide.

Container

For a slideshow, we need a container that can:

  • contain all slides.
  • hide slides that outside viewport of the container.
  • sent a signal that tells slides to move in.
  • sent a signal that tells slides to move out.
  • track which slide should be shown.

First, let's create a basic container with style:

export function Slideshow({ children, className, style }) {
  return (
      <div
        style={{
          height: "600px",
          width: "600px",
          position: "relative",
          overflow: "hidden"
        }}
      >
        {children}
      </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Second, let's add a context that will track activated slide and a timer:

const SlideshowContext = createContext();

export function Slideshow({ children, className, style }) {
  const [context, setContext] = useState({
    items: [],
    edge: false
  });
  const timer = useRef(null);

  useEffect(() => {
    if (timer.current) clearTimeout(timer.current);
    timer.current = setTimeout(() => {
      // Move deactivated slide out when edge is false
      // Move activated slide in when edge is true
      if (context.items.length > 1 && context.edge) {
        const head = context.items.shift();
        context.items.push(head);
      }
      context.edge = !context.edge;
      setContext({ ...context });
    }, 2500);

    return () => clearTimeout(timer.current);
  });

  console.log(context.items);

  return (
    <SlideshowContext.Provider value={[context, setContext]}>
      <div
        style={{
          height: "600px",
          width: "600px",
          position: "relative",
          overflow: "hidden"
        }}
      >
        {children}
      </div>
    </SlideshowContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

And that's all for the container.

Slide

The slide will have at least three stages:

  • on stage - slide is in viewport and has highest z-index.
  • off stage - slide is in viewport and has lowest z-index
  • ready for stage - slide is moved out from viewport, and it's waiting at ready position.

The reason we don't move out the slide at "off stage", is because, for this slideshow, we want to move "off" slide after the "on" slide completely cover the "off" one.

Slide will update it's stage when received signal from the container, so it will like:

  1. slide at ready position.
  2. when signal come and the slide is activated, moved to viewport.
  3. when signal come and the current slide is not the activated one, lower the z-index.
  4. when next signal come, move to ready position.

So, we can have something like this:

export function SlideshowItem({ children }) {
  const name = useRef(`${performance.now()}_${Math.random()}`);
// Generate a name for this slide.
  const [context] = useContext(SlideshowContext);
  const [stage, setStage] = useState("ready");

  useEffect(() => {
    // register self with the name.
    context.items.push(name.current);
    return () => {
      // Remove the name when slide is removed.
      const index = context.items.indexOf(name.current);
      context.items.splice(index, 1);
    };
  }, []);

  useEffect(() => {
    const activeName = context.items[0];
    if (activeName === name.current) {
      setStage("on");
    }
    if (activeName !== name.current && stage === "on") {
      setStage("off");
    }
    if (activeName !== name.current && stage === "off") {
      setStage("ready");
    }
  }, [context]);

  let left = 0;
  let zIndex = 0;
  switch (stage) {
    case "ready":
      left = "100%";
      break;
    case "on":
      left = "0";
      zIndex = 1;
      break;
    case "off":
      zIndex = 0;
      break;
    default:
  }

  return (
    <div
      style={{
        transition: "0.5s",
        position: "absolute",
        top: 0,
        left: left,
        zIndex: zIndex
      }}
    >
      {children}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

And now, we have a simple Slideshow.
Thanks all!

Discussion (0)