首页 > 解决方案 > 反应如何等待组件/页面呈现调用函数

问题描述

我需要修改这里返回的第一个 div 的 data-scroll 属性。问题似乎是种族危险 - 因为 dom 尚未加载,所以 active 为 null。我该如何处理?异步/等待?我没有查询任何内容,也不会得到回复。我如何等到 Header 被渲染?

这会生成 TypeError:无法读取 null 的属性“数据集”:

import { Link } from 'react-router-dom';
import * as ROUTES from '../constants/routes';

export default function Header() {
  const active = document.getElementById('navbar');

  const debounce = (fn) => {
    let frame;
    return (...params) => {
      if (frame) {
        cancelAnimationFrame(frame);
      }
      frame = requestAnimationFrame(() => {
        fn(...params);
      });
    };
  };
  const storeScroll = () => {
    if (window.scrollY > 80) {
      active.dataset.scroll = window.scrollY;
    }
    if (window.scrollY <= 80) {
      active.dataset.scroll = 0;
    }
  };

  // Listen for new scroll events
  document.addEventListener('scroll', debounce(storeScroll), { passive: true });

  // Update scroll position for first time
  storeScroll();
  return (
    <div id="navbar" data-scroll="0">
      <header className="fixed flex top-0 scroll:bg-blue-500 bg-transparent items-center justify-center p-5 w-full">
        <div className="container mx-auto max-w-screen-lg h-full">
          <div className="flex justify-between h-full">
            <div className="text-black text-center flex items-center align-items cursor-pointer">
              <h1 className="flex justify-center w-full">
                <Link to={ROUTES.TEST} className="font-bold text-lg" aria-label="home">
                  HOME
                </Link>
              </h1>
            </div>
          </div>
        </div>
      </header>
    </div>
  );
}

标签: javascriptreactjs

解决方案


在 react 中直接定位底层 DOM 并不是一个好习惯。我们可以使用ref来做到这一点。

此外,当组件卸载时,您正在添加一个eventListener未正确删除的组件。所以为了确保在渲染完成时你有元素,你可以使用useEffect钩子来保证它在绘制 DOM 之后运行。

所以你需要让你的 eventListener 代码在 useEffect hook 中移动。Ref这是使用和的更新代码useEffect

import {Link} from 'react-router-dom';
import * as ROUTES from '../constants/routes';
import {useRef, useEffect} from 'react';

export default function Header() {
  const navbarRef = useRef();

  const debounce = (fn) => {
    let frame;
    return (...params) => {
      if (frame) {
        cancelAnimationFrame(frame);
      }
      frame = requestAnimationFrame(() => {
        fn(...params);
      });
    };
  };

  const storeScroll = () => {
    if (window.scrollY > 80) {
      navbarRef.current.dataset.scroll = window.scrollY;
    }
    if (window.scrollY <= 80) {
      navbarRef.current.dataset.scroll = 0;
    }
  };

  useEffect(() => {
    // Update scroll position for first time
    storeScroll();
    
    // attact the event listener
    window.addEventListener('scroll', debounce(storeScroll), {passive: true});
    
    // remove the event listener when the component is unmounted
    return () =>
      window.removeEventListener('scroll', debounce(storeScroll), {
        passive: true,
      });
  }, []);

  return (
    <div id="navbar" ref={navbarRef} data-scroll="0">
      <header className="fixed flex top-0 scroll:bg-blue-500 bg-transparent items-center justify-center p-5 w-full">
        <div className="container mx-auto max-w-screen-lg h-full">
          <div className="flex justify-between h-full">
            <div className="text-black text-center flex items-center align-items cursor-pointer">
              <h1 className="flex justify-center w-full">
                <Link
                  to={ROUTES.TEST}
                  className="font-bold text-lg"
                  aria-label="home"
                >
                  HOME
                </Link>
              </h1>
            </div>
          </div>
        </div>
      </header>
    </div>
  );
}

推荐阅读