繁体   English   中英

React Hooks - 设置最后一个值?

[英]React Hooks - set last value?

我正在尝试编写基本的钩子来在滚动时获取 currentScroll、lastScroll、scrollSpeed。

function useDocScroll() {
  const isClient = typeof window === "object"

  function getScroll() {
    return isClient
      ? window.pageYOffset || document.documentElement.scrollTop
      : undefined
  }

  const [docScroll, setDocScroll] = useState(getScroll)
  const [lastScroll, setLastScroll] = useState(null)
  const [scrollSpeed, setScrollSpeed] = useState(Math.abs(docScroll - lastScroll))

  useEffect(() => {
    if (!isClient) {
      return false
    }

    function handleScroll() {
      setDocScroll(getScroll())
      setLastScroll(getScroll())  // <-- why is this working?
      // setLastScroll(docScroll) // <-- why is this not working?

      setScrollSpeed(Math.abs(docScroll - lastScroll)) // <-- why is this not working?
    }

    window.addEventListener("scroll", handleScroll)
  }, [])

  return [docScroll, lastScroll, scrollSpeed]
}

似乎当我执行setLastScroll(getScroll()) ,它可以很好地保存最后一个滚动值。

但我不明白,因为当handleScroll()触发时, getScroll()值不应该保持不变吗? 我不明白为什么setDocScroll(getScroll())setLastScroll(getScroll())有不同的值。

另外,我想我可以做setLastScroll(docScroll) ,意思是“用当前的 docScroll 值设置 lastScroll 值”,但它只是在 docScroll 值更改时打印“0”。

为什么是这样? 我想更好地理解。

+) 而且我无法获得由docScrolllastScroll计算的scrollSpeed ,但我不知道如何获得这些值。

编辑分心-golick-c89w3

我认为为什么您的代码不起作用是因为以下两个原因:

  1. setDocScroll之后直接使用docScroll将不起作用,因为 setState 是异步任务。 不能保证在执行下docScroll语句之前更新docScroll
  2. 由于在某些特定元素(可能)内发生滚动,您得到0 由于document.documentElement指向html元素并且里面没有滚动。 所以你收到0

解决方案:

您不需要多个useState 由于滚动事件发射太频繁,我认为使用useReducer来减少渲染次数是个好主意。 无论是在根级别还是在某个元素内部,了解滚动发生的位置很重要。

对于以下解决方案,我提出了:

如果滚动发生在根级别(html 元素),则无需将元素传递给useDocScroll 如果滚动发生在特定元素内,则需要传递元素引用。

const initState = {
  current: 0,
  last: 0,
  speed: 0,
};

function reducer(state, action) {
  switch (action.type) {
    case "update":
      return {
        last: state.current,
        current: action.payload,
        speed: Math.abs(action.payload - state.current) || 0,
      };
    default:
      return state;
  }
}

const isClient = () => typeof window === "object";
function useDocScroll(element = document.documentElement) {
  const [{ current, last, speed }, dispatch] = useReducer(reducer, initState);

  function getScroll() {
    return isClient() ? element.scrollTop : 0;
  }

  function handleScroll() {
    dispatch({ type: "update", payload: getScroll() });
  }

  useEffect(() => {
    if (!isClient()) {
      return false;
    }

    element.addEventListener("scroll", handleScroll);

    return () => element.removeEventListener("scroll", handleScroll);
  }, []);

  return [current, last, speed];
}

例子:

如果滚动发生在窗口内

const {current, last, speed} = useDocScroll()

如果滚动发生在特定元素

const {current, last, speed} = useDocScroll(document.getElementById("main"))

由于关闭,它不起作用:

useEffect(() => {
  if (!isClient) {
    return false;
  }

  function handleScroll() {
    setDocScroll(getScroll());

    // On calling handleScroll the values docScroll & lastScroll
    // are always will be of the first mount,
    // the listener "remembers" (closures) such values and never gets updated
    setLastScroll(docScroll);
    setScrollSpeed(Math.abs(docScroll - lastScroll));

    // v Gets updated on every call
    setLastScroll(getScroll());
  }

  // v Here you assigning a callback which closes upon the lexical scope of
  // docScroll and lastScroll
  window.addEventListener('scroll', handleScroll);
}, []);

要修复它,一个可能的解决方案可以是参考( useRef )和功能setState

例如:

setScrollSpeed(lastScroll => Math.abs(getScroll() - lastScroll))

不知道为什么lastScrolled需要:

function useDocScroll() {
  const [docScroll, setDocScroll] = useState(getScroll());
  const lastScrollRef = useRef(null);
  const [scrollSpeed, setScrollSpeed] = useState();

  useEffect(() => {
    if (!isClient) {
      return false;
    }

    function handleScroll() {
      const lastScroll = lastScrollRef.current;
      const curr = getScroll();
      setScrollSpeed(Math.abs(getScroll() - (lastScroll || 0)));
      setDocScroll(curr);

      // Update last
      lastScrollRef.current = curr;
    }

    window.addEventListener('scroll', handleScroll);
  }, []);

  return [docScroll, lastScrollRef.current, scrollSpeed];
}

编辑 hidden-architecture-p8xbi

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM