如何创建 React hook 来跟踪页面滚动深度?

问题描述 投票:0回答:1

我正在开发一个钩子,用于跟踪用户向下滚动页面的最大深度。 我想在 useEffect 的清理函数中使用这个值,以便我可以在卸载组件时记录该值。

该钩子似乎有效,但 Home 组件内的实现却无效。如果我填充依赖项数组,它会在滚动时连续记录,或者在组件卸载时记录一个 0。

我希望它在组件卸载时记录单个正确的值。

谢谢。

import { useEffect, useRef } from 'react';

export const useTrackScrollDepth = () => {
  const scrollDepthRef = useRef(0);

  const trackScrollDepth = () => {
    const scrolledValue = window.scrollY;

    scrollDepthRef.current = Math.max(scrolledValue, scrollDepthRef.current);
  };

  useEffect(() => {
    const handleScrollTracking = () => {
      trackScrollDepth();
    };

    document.addEventListener('scroll', handleScrollTracking);

    return () => {
      document.removeEventListener('scroll', handleScrollTracking);
    };
  }, []);

  return scrollDepthRef.current;
};

然后在我的组件中,我这样使用钩子:

import React, { useEffect } from 'react';

import { useTrackScrollDepth } from 'hooks/useTrackScrollDepth';

export function Home() {
  
  const maxScrollDepth = useTrackScrollDepth();

  useEffect(
    () => () => {
      console.log(`Max scroll depth: ${maxScrollDepth}`);
    },
    [],
  );

  return (
    <div>Home placeholder</div>
  );
}
javascript reactjs react-hooks scroll
1个回答
0
投票

当您在挂钩中使用

return scrollDepthRef.current;
时,它会返回每个渲染的当前编号。由于钩子不会导致重新渲染,因此该数字保持为 0。

当您将其添加到

useEffect
的依赖项时,滚动深度发生变化,并且某些原因导致重新渲染,
useEffect
清理被触发,并显示之前的值。

而是返回引用本身

return scrollDepthRef;
,并在
useEffect
中使用它。

const { useEffect, useRef, useCallback, useState } = React;

const useTrackScrollDepth = () => {
  const scrollDepthRef = useRef(0);

  useEffect(() => {
    const handleScrollTracking = () => {
      const scrolledValue = window.scrollY;

      scrollDepthRef.current = Math.max(scrolledValue, scrollDepthRef.current);
    };

    document.addEventListener('scroll', handleScrollTracking);

    return () => {
      document.removeEventListener('scroll', handleScrollTracking);
    };
  }, []);

  return scrollDepthRef; // return the reference and not the current value
};

function Home() {
  const scrollDepthRef = useTrackScrollDepth();
  
  useEffect(() => () => {
    console.log(`Max scroll depth: ${scrollDepthRef.current}`); // get the current value
  }, [scrollDepthRef]); // use the ref as dependency

  return (
    <div className="home">Home placeholder</div>
  );
}

const Demo = () => {
  const [show, setShow] = useState(true);
  
  const toggle = () => setShow(s => !s);

  return (
    <div>
      <button onClick={toggle}>Toggle</button>
      
      {show && <Home />}
    </div>
  );
};

ReactDOM
  .createRoot(root)
  .render(<Demo />);
button {
  position: fixed;
}

.home {
  height: 200vh;
  background: red;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="root"></div>

另一种选择是从钩子返回一个记忆函数,并在卸载时调用它:

const { useEffect, useRef, useCallback, useState } = React;

const useTrackScrollDepth = () => {
  const scrollDepthRef = useRef(0);

  useEffect(() => {
    const handleScrollTracking = () => {
      const scrolledValue = window.scrollY;

      scrollDepthRef.current = Math.max(scrolledValue, scrollDepthRef.current);
    };

    document.addEventListener('scroll', handleScrollTracking);

    return () => {
      document.removeEventListener('scroll', handleScrollTracking);
    };
  }, []);

  return useCallback(() => scrollDepthRef.current, []); // return a memoized function that returns the current value
};

function Home() {
  const getMaxScrollDepth = useTrackScrollDepth();
  
  useEffect(() => () => {
    console.log(`Max scroll depth: ${getMaxScrollDepth()}`); // get the current value
  }, [getMaxScrollDepth]);

  return (
    <div className="home">Home placeholder</div>
  );
}

const Demo = () => {
  const [show, setShow] = useState(true);
  
  const toggle = () => setShow(s => !s);

  return (
    <div>
      <button onClick={toggle}>Toggle</button>
      
      {show && <Home />}
    </div>
  );
};

ReactDOM
  .createRoot(root)
  .render(<Demo />);
button {
  position: fixed;
}

.home {
  height: 200vh;
  background: red;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="root"></div>

© www.soinside.com 2019 - 2024. All rights reserved.