Search code examples
javascriptweb-componentstenciljs

Is it possible to lazy render component in StencilJS?


As you want to build complex component, it would be great if you can wrap any DOM with component such as "lazy-load" component with condition (@Prop() condition: boolean) so to illustrate what I want:

<lazy-load condition={some boolean condition, like certain link get clicked and section is now active}>
  <data-fetch>
  </data-fetch>
</lazy-load>

in this example, "data-fetch" will make a HTTP call to grab some large data, and I want to defer this component added to DOM until condition we specify in the lazy-load component to be true.

So I started to implement render() of lazy-load component as something along the line of

@Prop() condition: boolean;

render() {
  if(!this.condition) {
    return null;
  }
  return (
    <slot/>
  );
}

and try to use it as

<lazy-load condition={false}>
  <data-fetch>
  </data-fetch>
</lazy-load>

but no matter what I tried, data-fetch component get added to DOM (and while we can set visibility to hide element, we would waste HTTP call) I understand I can put the same condition in the data-fetch itself and then not make a fetch call when condition is false, but if possible I want generic wrapper component to achieve this (if you are familiar with AngularJS or Angular, I want to find a way to do equivalent of ng-if and *ngIf off of generic wrapper component)

Maybe this is a limitation due to how "slot" tag supposed to work? (also, I'm using it with @Component({shadow: false}) so I know I'm not using standard shadowDOM from the web component spec so maybe what I'm trying to do is not feasible?

Thank you very much for your time in advance to even read this question and I appreciate any help I can get. I feel if we can do this, we might be able to build component that can quickly differ loading until whenever we feel it should load/render.


Solution

  • Yeah it's an issue with not using shadow: true, because in the polyfill the slotted content just becomes part of the light DOM (but gets placed where the slot element is). Beware that even if you enable Shadow DOM, it'll still fallback to the polyfill if the browser doesn't support it. You could raise an issue about this in Github but I'm not sure if/how it would be possible to solve this "dynamic slot" problem.

    But I think you can take a simpler approach:

    {myCondition && <data-fetch />}
    

    That way the data-fetch element will only be added once the condition becomes true.

    You could also refactor this into a functional component:

    import { FunctionalComponent } from '@stencil/core';
    
    interface Props {
      if: boolean;
    }
    
    export const LazyLoad: FunctionalComponent<Props> = ({ if }, children) =>
      if && children;
    
    import { LazyLoad } from './LazyLoad';
    
    <LazyLoad if={condition}>
      <data-fetch />
    </LazyLoad>