Loader na pełny ekran

0

Hej, chce dodac loader na pelny ekran do mojej aplikacji w NextJS, loader taki jak tutaj na click: https://jsfiddle.net/Lvd9nkwx/2/

Zrobilem tak:

'use client';

import { useLayoutEffect } from 'react';

export function Loader() {
  useLayoutEffect(() => {
    const fpl = document.getElementById('full-page-loader');
    const onLoad = () => {
      fpl!.classList.add('hide');
    };

    if (document.readyState === 'complete') {
      onLoad();
    } else {
      window.addEventListener('load', onLoad);
      return () => window.removeEventListener('load', onLoad);
    }
  }, []);

  return (
    <div id='full-page-loader'>
      <svg
        height='76'
        viewBox='0 0 12 10'
        fill='none'
        xmlns='http://www.w3.org/2000/svg'
      >
        <path
          d='M1.33346 8.50746C1.14794 8.50746 0.974009 8.47302 0.811674 8.40413C0.649339 8.33525 0.50633 8.23957 0.382646 8.11711C0.266693 7.99464 0.17393 7.85687 0.104358 7.70379C0.034786 7.54305 0 7.37084 0 7.18714V0H0.881246V7.18714C0.881246 7.31726 0.923762 7.42442 1.00879 7.50861C1.09383 7.5928 1.20205 7.6349 1.33346 7.6349H1.43782C1.56924 7.6349 1.67746 7.5928 1.76249 7.50861C1.84752 7.42442 1.89004 7.31726 1.89004 7.18714V2.81286C1.89004 2.69039 1.84752 2.58706 1.76249 2.50287C1.67746 2.41102 1.56924 2.3651 1.43782 2.3651H1.11315V1.49254H1.43782C1.62335 1.49254 1.79341 1.52698 1.94802 1.59587C2.11035 1.66475 2.25336 1.76043 2.37704 1.88289C2.50073 2.00536 2.59736 2.14696 2.66693 2.30769C2.7365 2.46077 2.77129 2.62916 2.77129 2.81286V7.18714C2.77129 7.37084 2.7365 7.54305 2.66693 7.70379C2.59736 7.85687 2.50073 7.99464 2.37704 8.11711C2.25336 8.23957 2.11035 8.33525 1.94802 8.40413C1.79341 8.47302 1.62335 8.50746 1.43782 8.50746H1.33346Z'
          fill='white'
        />
        <path
          d='M3.93834 4.55798H4.94713V2.81286C4.94713 2.69039 4.90462 2.58706 4.81959 2.50287C4.73455 2.41102 4.62633 2.3651 4.49492 2.3651H4.39056C4.25914 2.3651 4.15092 2.41102 4.06589 2.50287C3.98086 2.58706 3.93834 2.69039 3.93834 2.81286V4.55798ZM4.39056 8.50746C4.20503 8.50746 4.0311 8.47302 3.86877 8.40413C3.70643 8.33525 3.56342 8.23957 3.43974 8.11711C3.32379 7.99464 3.23102 7.85687 3.16145 7.70379C3.09188 7.54305 3.05709 7.37084 3.05709 7.18714V2.81286C3.05709 2.62916 3.09188 2.46077 3.16145 2.30769C3.23102 2.14696 3.32379 2.00536 3.43974 1.88289C3.56342 1.76043 3.70643 1.66475 3.86877 1.59587C4.0311 1.52698 4.20503 1.49254 4.39056 1.49254H4.49492C4.68044 1.49254 4.85437 1.52698 5.01671 1.59587C5.17904 1.66475 5.31818 1.76043 5.43414 1.88289C5.55782 2.00536 5.65445 2.14696 5.72402 2.30769C5.80132 2.46077 5.83998 2.62916 5.83998 2.81286V5.44202H3.93834V7.18714C3.93834 7.31726 3.98086 7.42442 4.06589 7.50861C4.15092 7.5928 4.25914 7.6349 4.39056 7.6349H4.49492C4.62633 7.6349 4.73455 7.5928 4.81959 7.50861C4.90462 7.42442 4.94713 7.31726 4.94713 7.18714V6.75086H5.83998V7.18714C5.83998 7.37084 5.80132 7.54305 5.72402 7.70379C5.65445 7.85687 5.55782 7.99464 5.43414 8.11711C5.31818 8.23957 5.17904 8.33525 5.01671 8.40413C4.85437 8.47302 4.68044 8.50746 4.49492 8.50746H4.39056Z'
          fill='white'
        />
        <path
          d='M7.01835 4.55798H8.02715V2.81286C8.02715 2.69039 7.98463 2.58706 7.8996 2.50287C7.81457 2.41102 7.70634 2.3651 7.57493 2.3651H7.47057C7.33916 2.3651 7.23093 2.41102 7.1459 2.50287C7.06087 2.58706 7.01835 2.69039 7.01835 2.81286V4.55798ZM7.47057 8.50746C7.28504 8.50746 7.11111 8.47302 6.94878 8.40413C6.78644 8.33525 6.64344 8.23957 6.51975 8.11711C6.4038 7.99464 6.31104 7.85687 6.24146 7.70379C6.17189 7.54305 6.13711 7.37084 6.13711 7.18714V2.81286C6.13711 2.62916 6.17189 2.46077 6.24146 2.30769C6.31104 2.14696 6.4038 2.00536 6.51975 1.88289C6.64344 1.76043 6.78644 1.66475 6.94878 1.59587C7.11111 1.52698 7.28504 1.49254 7.47057 1.49254H7.57493C7.76045 1.49254 7.93438 1.52698 8.09672 1.59587C8.25905 1.66475 8.3982 1.76043 8.51415 1.88289C8.63783 2.00536 8.73446 2.14696 8.80403 2.30769C8.88134 2.46077 8.91999 2.62916 8.91999 2.81286V5.44202H7.01835V7.18714C7.01835 7.31726 7.06087 7.42442 7.1459 7.50861C7.23093 7.5928 7.33916 7.6349 7.47057 7.6349H7.57493C7.70634 7.6349 7.81457 7.5928 7.8996 7.50861C7.98463 7.42442 8.02715 7.31726 8.02715 7.18714V6.75086H8.91999V7.18714C8.91999 7.37084 8.88134 7.54305 8.80403 7.70379C8.73446 7.85687 8.63783 7.99464 8.51415 8.11711C8.3982 8.23957 8.25905 8.33525 8.09672 8.40413C7.93438 8.47302 7.76045 8.50746 7.57493 8.50746H7.47057Z'
          fill='white'
        />
        <path
          d='M10.11 10V9.12744H10.6549C10.7864 9.12744 10.8946 9.08534 10.9796 9.00115C11.0724 8.92461 11.1188 8.82128 11.1188 8.69116V2.81286C11.1188 2.69039 11.0724 2.58706 10.9796 2.50287C10.8946 2.41102 10.7864 2.3651 10.6549 2.3651H10.5506C10.4192 2.3651 10.3109 2.41102 10.2259 2.50287C10.1409 2.58706 10.0984 2.69039 10.0984 2.81286V7.18714C10.0984 7.31726 10.1409 7.42442 10.2259 7.50861C10.3109 7.5928 10.4192 7.6349 10.5506 7.6349H10.8753V8.50746H10.5506C10.3651 8.50746 10.1911 8.47302 10.0288 8.40413C9.86646 8.33525 9.72345 8.23957 9.59976 8.11711C9.48381 7.99464 9.39105 7.85687 9.32148 7.70379C9.2519 7.54305 9.21712 7.37084 9.21712 7.18714V2.81286C9.21712 2.62916 9.2519 2.46077 9.32148 2.30769C9.39105 2.14696 9.48381 2.00536 9.59976 1.88289C9.72345 1.76043 9.86646 1.66475 10.0288 1.59587C10.1911 1.52698 10.3651 1.49254 10.5506 1.49254H10.6549C10.8405 1.49254 11.0144 1.52698 11.1767 1.59587C11.3391 1.66475 11.4821 1.76043 11.6058 1.88289C11.7294 2.00536 11.8261 2.14696 11.8956 2.30769C11.9652 2.46077 12 2.62916 12 2.81286V8.69116C12 8.87486 11.9652 9.04325 11.8956 9.19633C11.8261 9.35706 11.7294 9.49483 11.6058 9.60964C11.4821 9.73211 11.3391 9.82778 11.1767 9.89667C11.0144 9.96556 10.8405 10 10.6549 10H10.11Z'
          fill='white'
        />
      </svg>
    </div>
  );
}


I w Layout.tsx

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang='en'>
      <body className={inter.className}>
        <ThemeProvider
          attribute='class'
          defaultTheme='system'
          enableSystem
          disableTransitionOnChange
        >
          <GlobalProviders>{children}</GlobalProviders>
        </ThemeProvider>
        <Loader />
        <Toaster />
      </body>
    </html>
  );
}

Dziala ale niestety bez animacji jak w przykladzie :( Co powinienem zmienic?

1

const fpl = document.getElementById('full-page-loader');
const onLoad = () => {
fpl!.classList.add('hide');
};

Ale dlaczego nie używasz Reacta do manipulacji klasami?
Zwykle się to robi tak, że ustawia się stan w React, to powoduje przerenderowanie komponentu. A renderując komponent możesz ustawić od razu klasę wg stanu:

const [shouldHide, setShouldHide] = React.useState(false);
// ...
<div id='full-page-loader' className={shouldHide? 'hide' : ''}>

Jeśli robisz partyzantkę niezgodną z założeniami Reacta, to potem nie dziwne, że ci coś nie działa.

0

Nie ma żadnego błędu w konsoli? Hook useLayoutEffect powinien teoretycznie uruchomić się przed załadowaniem komponentu, więc document.getElementById według mnie powinien zwrócić null.

EDIT:
Doczytałem dokumentację i działa to lekko inaczej niż myślałem :D
Komponent renderowany jest normalnie, ale "nie jest widoczny" póki useLayoutEffect się nie wykona.

0
LukeJL napisał(a):

const fpl = document.getElementById('full-page-loader');
const onLoad = () => {
fpl!.classList.add('hide');
};

Ale dlaczego nie używasz Reacta do manipulacji klasami?
Zwykle się to robi tak, że ustawia się stan w React, to powoduje przerenderowanie komponentu. A renderując komponent możesz ustawić od razu klasę wg stanu:

const [shouldHide, setShouldHide] = React.useState(false);
// ...
<div id='full-page-loader' className={shouldHide? 'hide' : ''}>

Jeśli robisz partyzantkę niezgodną z założeniami Reacta, to potem nie dziwne, że ci coś nie działa.

Zrobilem tak:

'use client';

import { useEffect, useState, useLayoutEffect, useTransition } from 'react';

export function Loader() {
  useLayoutEffect(() => {
    const onLoad = () => {
      setShouldHide(true);
    };

    if (document.readyState === 'complete') {
      onLoad();
    } else {
      window.addEventListener('DOMContentLoaded', onLoad);
      return () => window.removeEventListener('DOMContentLoaded', onLoad);
    }
  }, []);

  useEffect(() => {}, []);
  const [shouldHide, setShouldHide] = useState(false);

  return (
    <div id='full-page-loader' className={shouldHide ? 'hide' : ''}>
      <div className='logo text-center text-[50px]'>LoaderContent...</div>
    </div>
  );
}

Ale dalej animacja nie dziala. Co Ciekawe jak dodam timeout do mojej wczesniejszej wersji:

export function Loader() {
  useLayoutEffect(() => {
    const fpl = document.getElementById('full-page-loader');
    const onLoad = () => {
      setTimeout(function () {
        fpl!.classList.add('hide');
      }, 1);
    };

    if (document.readyState === 'complete') {
      onLoad();
    } else {
      window.addEventListener('DOMContentLoaded', onLoad);
      return () => window.removeEventListener('DOMContentLoaded', onLoad);
    }
  }, []);

  useEffect(() => {}, []);
  const [isPending, startTransition] = useTransition();

  const [loading, setLoading] = useState<boolean>(false);

  return (
    <div id='full-page-loader'>
      <div className='logo text-center text-[50px]'>Loading content...</div>
    </div>
  );
}


Nie kumam dlaczego i nie wydaje mi sie to eleganckie rozwiazanie ale moze tak ma byc?

Xarviel napisał(a):

Nie ma żadnego błędu w konsoli? Hook useLayoutEffect powinien teoretycznie uruchomić się przed załadowaniem komponentu, więc document.getElementById według mnie powinien zwrócić null.

Nie ma zadnego bledu

1 użytkowników online, w tym zalogowanych: 0, gości: 1