spectacle icon indicating copy to clipboard operation
spectacle copied to clipboard

unmount slides when not displayed

Open sneakers-the-rat opened this issue 4 years ago • 6 comments

Description

Currently all slides remain in the DOM with display:none when not displayed & the components remain mounted. this means any slides that have components with side effects like animations will continue running, and everything starts going very sloooow.

Proposal

Add a prop to Deck like bufferRange where the content of slides slides +/- that range are mounted in the Dom, but otherwise are unmounted.

sneakers-the-rat avatar May 08 '21 04:05 sneakers-the-rat

since the progress bar is now also rendered for every slide, once you have appreciable slides you end up rendering an n^2 number of elements if you include it -- eg for a slideshow of 40 slides that's 1600 additional elements in the DOM. I haven't profiled but my slideshows are significantly laggier when I have any variant of a progress bar object (the base progress bar included)

sneakers-the-rat avatar May 12 '21 07:05 sneakers-the-rat

it would be nice to be able to render an object that can access the DeckContext once rather than needing to include something on every slide. when I try and use DeckContext at the level of the deck though it has reference errors from unmounted slides. this seems like a pretty common use case (eg. progress bars, contact info, content overlays that persist between slides) that would make me elevate this issue from a feature request to a bug since the performance hit seems to be so massive

sneakers-the-rat avatar May 12 '21 07:05 sneakers-the-rat

This works except for that now all the intra-slide steps are broken, that must be computed when the deck is initially mounted?

https://github.com/sneakers-the-rat/infrastructure-presentation/blob/e78c43ef482aa104cad05b63371fa473d6613696/src/components/hideslide.js#L1-L40

sneakers-the-rat avatar May 13 '21 21:05 sneakers-the-rat

rendering them on first mount of Deck (by adding a || !initialized check) seems to fix the number of steps, and all my custom stepper components work, but now Appear components are broken because they seem to be tied to a specific stepID rather than a step #

edit: confirmed that providing an explicit 'id' to Appear fully fixes and everything is way way way way faster

sneakers-the-rat avatar May 13 '21 21:05 sneakers-the-rat

First off: you're absolutely right about Progress, and I'm kind of annoyed at myself for not catching it. 😅

Secondly: your suggestion absolutely holds water. The problems you're running into most likely have to do with the fact that Slides need to actually render their contents in order to detect "step participants". Moreover, as you've noticed: slides aren't exactly aware of their location in the presentation- they only know that the Deck addresses them by their slide ID. So the solution is more or less to have the Deck keep track of which slides should be rendered and which slides shouldn't, and signal them accordingly.

You could technically implement a fix with some creative usage of DeckContext and your HideSlide component, but it's much easier to implement in-core. So I'm cutting that PR now.

mhink avatar Jul 01 '21 21:07 mhink

Here is a simple way to only mount a slide's content if it is active.

First create a wrapper around Slide like this:

import { useContext, useId } from "react";
import { DeckContext, Slide as SpectacleSlide } from "spectacle";

export const Slide = (props: React.ComponentProps<typeof SpectacleSlide>) => {
  const randomId = useId();
  const id = props.id ?? randomId;
  const { activeView } = useContext(DeckContext);
  return (
    <SpectacleSlide id={id}>
      {id === activeView.slideId && props.children}
    </SpectacleSlide>
  );
};

and then use it in your deck like this:

import { Deck, Heading, DefaultTemplate } from "spectacle";
import { Slide } from "./shared";

export const App = () => {
  return (
    <Deck template={<DefaultTemplate />}>
      <Slide>
        <Heading>My Slide</Heading>
        <MyComponent />
      </Slide>
     {/* and so on...*/}
    </Deck>
  );
};

Suggestion: this logic could be added to the Slide component based on isActive flag in https://github.com/FormidableLabs/spectacle/blob/main/packages/spectacle/src/components/slide/slide.tsx#L438 and then add it as optional props to Slide <Slide unmountInactive=true /> or <Deck unmountInactive=true />

shipplix avatar Jan 22 '24 04:01 shipplix