2
votes

React Hooks, comment implémenter le hook useHideOnScroll?

if (scrolledDown && !isHidden) {
        setIsHidden(true);
      } else if (scrolledUp && isHidden) {
        setIsHidden(false);
      }

0 commentaires

4 Réponses :


2
votes

Vous devez utiliser window.addEventListener et https://reactjs.org/docs/hooks-custom.html guide.

C'est mon exemple de travail:

import React, { useState, useEffect } from "react";

const useHideOnScrolled = () => {
  const [hidden, setHidden] = useState(false);

  const handleScroll = () => {
    const top = window.pageYOffset || document.documentElement.scrollTop;
    setHidden(top !== 0);
  };

  useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return hidden;
};

export default useHideOnScrolled;

démo en direct: https://codesandbox.io/s/w0p3xkoq2l?fontsize=14

et je pense que le nom useIsScrolled() ou quelque chose comme ça serait mieux


3 commentaires

Appréciez le temps mis pour répondre! Je ne pense pas que ce soit complet cependant. Votre solution ne fonctionne simplement qu'en haut de la page, ce qui n'est pas le but. Le but est de travailler tout le temps, quel que soit le défilement de l'utilisateur.


Vous pouvez vérifier la réponse de jayarjo. Bien qu'il ne soit pas optimal à 100%, fonctionne comme prévu


@ZenVentzi oh, je comprends. Je vais réessayer maintenant et modifier ma réponse



1
votes

Après des heures de balançoire, voici ce que j'ai trouvé.

const shouldHide = useHideOnScroll();
return shouldHide ? null : <div>something</div>

Usage:

const useHideOnScroll = () => {
  const [isHidden, setIsHidden] = useState(false);
  const prevScrollY = useRef<number>();

  useEffect(() => {
    const onScroll = () => {
      const scrolledDown = window.scrollY > prevScrollY.current!;
      const scrolledUp = !scrolledDown;

      if (scrolledDown && !isHidden) {
        setIsHidden(true);
      } else if (scrolledUp && isHidden) {
        setIsHidden(false);
      }

      prevScrollY.current = window.scrollY;
    };

    window.addEventListener("scroll", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
    };
  }, [isHidden]);

  return isHidden;
};

Il est toujours sous-optimal, car nous réaffectons le onScroll lorsque le isHidden change. Tout le reste semblait trop piraté et non documenté. Je suis vraiment intéressé à trouver un moyen de faire de même, sans réattribuer onScroll . Commentez si vous connaissez un moyen :)


1 commentaires

Vous pouvez vous déplacer vers le haut avec isHidden comme paramètre pour ne pas avoir à le réaffecter à chaque fois



2
votes

Ici:

.navbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 30px;
  background: rgba(255, 255, 255, 0.8);
}

Vous pourriez avoir des inquiétudes sur le fait que setIsHidden provoque le rendu à chaque onScroll , en renvoyant toujours une nouvelle valeur d'état, mais un setter de useState est assez intelligent pour se mettre à jour uniquement si la valeur a réellement changé.

De plus, votre .navbar ( j'y ai ajouté une classe ) ne devrait pas changer la mise en page lorsqu'il apparaît ou votre extrait de code sera verrouillé dans une boucle infinie. Voici également les styles appropriés pour cela:

const useHideOnScroll = () => {
  const prevScrollY = React.useRef<number>();
  const [isHidden, setIsHidden] = React.useState(false);

  React.useEffect(() => {
    const onScroll = () => {
      setIsHidden(isHidden => {
        const scrolledDown = window.scrollY > prevScrollY.current!;
        if (scrolledDown && !isHidden) {
          return true;
        } else if (!scrolledDown && isHidden) {
          return false;
        } else {
          prevScrollY.current = window.scrollY;
          return isHidden;
        }
      });
    };

    console.log("adding listener");
    window.addEventListener("scroll", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
    };
  }, []);

  return isHidden;
};

const Navbar = () => {
  const isHidden = useHideOnScroll();
  console.info("rerender");
  return isHidden ? null : <div className="navbar">navbar</div>;
};

export default Navbar;

Code complet de la boîte à sable: https://codesandbox.io/s/13kr4xqrwq


3 commentaires

Merci. Juste pour clarifier, dans l'instruction else, où nous retournons simplement isHidden, le composant est-il rendu ou non? Je suppose que non, car il voit que l'état n'a pas changé. Est-ce exact? Ce n'est absolument pas un problème, mais je suis curieux de savoir pourquoi l'exemple de code console.log('render') deux fois lors du changement d'état?


Remplacement par console.info(`rerender ${isHidden}`); nous obtenons une sortie telle que rerender true rerender true rerender false rerender false au lieu d'une seule fois. Pourquoi donc? Encore une fois, ce n'est pas un problème mais j'essaie de donner un sens à tout cela


La double chose est une sorte de mystère pour moi en ce moment. Je lirai la source quand j'aurai du temps libre pour voir ce qui se passe réellement. Pour le moment, vous pouvez onScroll gestionnaire onScroll (car il se déclenche plusieurs fois et je suppose que ce n'est de toute façon pas souhaitable).



2
votes

Utilisation de hooks dans React (16.8.0+)

import React, { useState, useEffect } from 'react'

function getWindowDistance() {
    const { pageYOffset: vertical, pageXOffset: horizontal } = window
    return {
        vertical,
        horizontal,
    }
}

export default function useWindowDistance() {
    const [windowDistance, setWindowDistance] = useState(getWindowDistance())

    useEffect(() => {
        function handleScroll() {
            setWindowDistance(getWindowDistance())
        }

        window.addEventListener('scroll', handleScroll)
        return () => window.removeEventListener('scroll', handleScroll)
    }, [])

    return windowDistance
}


0 commentaires