确定当前使用 react 和 Waypoint 支付的哪个部分

Determine which section is currently dispayed with react and Waypoint

提问人:Unnamed 提问时间:6/21/2023 最后编辑:Ahmed SbaiUnnamed 更新时间:6/26/2023 访问量:139

问:

我有非常简单的react应用程序:

App.js:

render() {
 return (
   <BaseLayout />
 );
}

基础布局:

import React, { useEffect, useState } from 'react';
import Navbar from "./Navbar";
import Section1 from "./section1/Section1";
import Section2 from "./section2/Section2";
import Section3 from "./section3/Section3";
import { Box, Grid } from "@mui/material";
import Fade from "react-awesome-reveal";
import { Waypoint } from 'react-waypoint';

我只是一次显示所有部分,然后用动画显示它们。一切都很好。但我也想突出显示当前显示的部分,并在滚动过程中相应地更改它(当前突出显示仅在单击期间发生)FadeNavbar

  return (
    <Box className={darkMode ? Style.dark : Style.light}>
      <Grid
        container
        display={"flex"}
        flexDirection={"column"}
        minHeight={"100vh"}
        justifyContent={"space-between"}
      >
        <Grid item>
          <Navbar darkMode={darkMode} handleClick={handleToggleDarkMode} />
        </Grid>
        <Grid item flexGrow={1} className={Style.firstContentRow}>
          <Section1 />
        </Grid>
        <Grid item flexGrow={1}>
          <Fade bottom triggerOnce={true} className={Style.secondContentRow}>
            <Section2 />
          </Fade>
        </Grid>
        <Grid item flexGrow={1} className={Style.firstContentRow}>
          <Fade bottom triggerOnce={true}>
            <Section3 />
          </Fade>
        </Grid>
        <Waypoint
          onEnter={({ previousPosition, currentPosition, event }) => {
            // PreviousPosition:below
            // CurrentPosition:inside
            // The above fields just return either `below` or `inside` and *NOT* for each section.
            console.log("PreviousPosition:" + previousPosition);
            console.log("CurrentPosition:" + currentPosition);

            // I see no usefull data here as well, did I miss something?
            console.log("Event:" + event);
          }}
        />
      </Grid>
    </Box>
  );

为此,我认为我需要使用他的一些事件,这应该让我知道现在显示的是哪个特定部分,然后相应地更改特定导航栏按钮上的类名。但是,航点事件提供的信息看起来还不够。我找不到如何理解此时显示哪个特定部分。我错过了什么吗?此外,正如我所看到的,该事件不是针对每个部分触发的,并且只针对最后一个部分触发Waypoint

javascript reactjs react-grid-layout 航点

评论


答:

2赞 Ahmed Sbai 6/25/2023 #1

要回答该问题,请执行以下操作:

<Waypoint/>无助于实现你想要的东西,它不是用来的,它实际上是一个你可以放在你的 jsx 中的组件。

适用于所有可以滚动的容器,包括窗口。

当您滚动时,您可以将其视为一个,一旦到达、离开或改变位置,就会触发相应的事件。
当您输入此点时,前一个位置始终在此处“下方”,因为您已经放在网格的末尾。
该对象对于您想要的内容也没有用,如果您选中,您将找到整个可滚动元素,而不是屏幕上最后显示的元素。
<Waypoint/>eventevent.doccument

event - 触发回调的本机滚动事件。如果回调不是由于滚动而触发的,则可能会丢失。

所以通常使用

构建延迟加载内容、无限滚动、滚动间谍或在滚动时将元素停靠到视口等功能。

溶液:

您可以在每个部分之前使用三个,并在一个状态之前使用一个状态来指示哪个是最后一个航点(显示的部分的顺序):<Waypoint/>

const [currentSection, setCurrentSection] = useState(1);

然后创建一个钩子,每次更新时都要运行:useEffectcurrentSection

useEffect(() => { 
    console.log("current section is updated and this is its order ", currentSection);
    // now you have the number indicates of the current section you can manage how to make your nav bar react to that  
}, [currentSection]);

JSX:

<Box>
  <Waypoint
    onEnter={() => {
      setCurrentSection(1);
    }}
  />
  <Grid
    container
    display={"flex"}
    flexDirection={"column"}
    minHeight={"100vh"}
    justifyContent={"space-between"}
  >
    <Grid
      item
      flexGrow={1}
      style={{ height: "800px", background: "red" }}
    >
      <div>section 1</div>
    </Grid>
    <Waypoint
      onEnter={() => {
        setCurrentSection(2);
      }}
    />
    <Grid
      item
      flexGrow={1}
      style={{ height: "800px", background: "white" }}
    >
      <div>section 2</div>
    </Grid>
    <Waypoint
      onEnter={() => {
        setCurrentSection(3);
      }}
    />
    <Grid
      item
      flexGrow={1}
      style={{ height: "800px", background: "green" }}
    >
      <div>section 3</div>
    </Grid>
  </Grid>
</Box>

备选方案(不带航点):

我曾经使用滚动和引用的事件侦听器来管理它。

您希望为每个部分容器提供 a 和 an,以及指示当前屏幕上哪个部分的状态:refidgrid

const firstRef = useRef();
const secondRef = useRef();
const ThirdRef = useRef();
const [currentSection, setCurrentSection] = useState();

当组件挂载到第一部分并设置事件侦听器时:setCurrentSection

useEffect(() => {
  setCurrentSection(firstRef.current);

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

每次滚动和更新状态时,该函数都会运行,但可能只有在获得新值时才会重新渲染组件:handleScrollcurrentSectioncurrentSection

const handleScroll = () => {
  const scrollPosition = window.scrollY || document.documentElement.scrollTop;
  const windowHeight = window.innerHeight;

  const sectionPositions = [
    firstRef.current,
    secondRef.current,
    ThirdRef.current
  ];

  const currentSection = sectionPositions.find(
    (section) =>
      scrollPosition >= section.offsetTop &&
      scrollPosition < section.offsetTop + windowHeight
  );

  if (currentSection) {
    setCurrentSection(currentSection);
  }
};

最后,我们创建一个钩子,每次更新 的值时都会触发该钩子:useEffectcurrentSection

useEffect(() => {
  if (currentSection) {
    console.log("current section is updated and this is the id of the current one: ", currentSection.id);
    // now you have the id of the current section you can manage how to make your nav bar react to that
  }
}, [currentSection]);

完整示例:

const Test = () => {
  const firstRef = useRef();
  const secondRef = useRef();
  const ThirdRef = useRef();

  const [currentSection, setCurrentSection] = useState();

  const handleScroll = () => {
    const scrollPosition = window.scrollY || document.documentElement.scrollTop;
    const windowHeight = window.innerHeight;
    const sectionPositions = [ firstRef.current, secondRef.current, ThirdRef.current ];
    const currentSection = sectionPositions.find(
      (section) =>
        scrollPosition >= section.offsetTop && scrollPosition < section.offsetTop + windowHeight
    );

    if (currentSection) {
      setCurrentSection(currentSection);
    }
  };

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

  useEffect(() => {
    if (currentSection) {
      console.log("current section is updated and this is the id of the current one: ", currentSection.id );
      // now you have the id of the current section you can manage how to make your nav bar react to that
    }
  }, [currentSection]);

  return (
    <div className="App">
      <Box>
        <Grid
          container
          display={"flex"}
          flexDirection={"column"}
          minHeight={"100vh"}
          justifyContent={"space-between"}
        >
          <Grid
            id={1}
            item
            flexGrow={1}
            style={{ height: "800px", background: "red" }}
            ref={firstRef}
          >
            <div>section 1</div>
          </Grid>
          <Grid
            id={2}
            item
            flexGrow={1}
            style={{ height: "800px", background: "white" }}
            ref={secondRef}
          >
            <div>section 2</div>
          </Grid>
          <Grid
            id={3}
            item
            flexGrow={1}
            style={{ height: "800px", background: "green" }}
            ref={ThirdRef}
          >
            <div>section 3</div>
          </Grid>
        </Grid>
      </Box>
    </div>
  );
};

评论

0赞 Unnamed 6/26/2023
第一种方法非常有效,第二种方法在滚动过程中抛出。Cannot read properties of undefined (reading 'offsetTop')
0赞 Unnamed 6/26/2023
但是在这一点上,我也对第一个没问题
1赞 Ahmed Sbai 6/26/2023
@Unnamed这两种方法对我来说都很好用,唯一的解释是滚动事件处理程序是在 useEffect 完成之前触发的,很奇怪!,但你仍然可以在此条件下将代码包装在里面,它应该可以工作Cannot read properties of undefined (reading 'offsetTop')handleScrollif(firstRef.current && secondRef.current && thirdRef.current)
1赞 Unnamed 6/29/2023
第二种方法现在也适用于我!