javascript 如何用useInView钩子识别React组件并将它们的值传递给子组件?

dauxcl2d  于 2023-03-16  发布在  Java
关注(0)|答案(3)|浏览(105)

我用React创建了一个导航栏,当用户滚动到视图中时,它会以可视方式突出显示活动组件。因此,他们滚动到“About”组件,用户图标会突出显示,主页图标会恢复正常。它看起来像这样:

我尝试分两步完成:(1)使用我的App.jsx组件中的useInView钩子,在用户滚动到useInView {ref}时,用useState钩子设置'activeElement',(2)通过props和useEffect将'activeElement'作为useState变量传递给子Nav组件,在用户滚动时更新Nav组件中的activeNav。
这是我的App组件的代码,我已经在段落标记中测试过了。目前,activeElement不受滚动的影响。

const App = () => {
  const { ref, inView, entry } = useInView({
    /* Optional options */
    threshold: 0,
  });

  const [activeElement, setActiveElement] = useState('#')
  
  return (
    <>
      <Header ref={ref} setActiveElement={inView ? '#home' : '' }/>
      <Nav activeElement={activeElement}/>
      <About ref={ref} setActiveElement={inView ? '#about' : '' } />
      <p>{activeElement}</p>
      <Services ref={ref} setActiveElement={inView ? '#services' : '' } />
      <Contact ref={ref} setActiveElement={inView ? '#contact' : '' }/>
      <Footer />
      <p>{activeElement}</p>
    </>
  )
}

export default App

下面是我的Nav组件的代码:

const Nav = ({activeElement}) => {
  const [activeNav, setActiveNav] = useState('#');
  
  useEffect(() => {
    setActiveNav(activeElement);
  })

  return (
    <nav>
      <a href="#" onClick={() => setActiveNav('#')} className={activeNav === '#' ? 'active' : ''}><AiOutlineHome /></a>
      <a href="#about" onClick={() => setActiveNav('#about')} className={activeNav === '#about' ? 'active' : ''}><AiOutlineUser /></a>
      <a href="#experience" onClick={() => setActiveNav('#experience')} className={activeNav === '#experience' ? 'active' : ''}><HiOutlineBookOpen /></a>
      <a href="#services" onClick={() => setActiveNav('#services')} className={activeNav === '#services' ? 'active' : ''}><FaUncharted /></a>
      <a href="#contact" onClick={() => setActiveNav('#contact')} className={activeNav === '#contact' ? 'active' : ''}><RiMessage2Line /></a>
    </nav>
  )
}

export default Nav

我的useInView执行有什么问题?我是否正确地将activeElement变量传递给了Nav组件?

感谢您花时间阅读本文。

wljmcqd8

wljmcqd81#

溶液:

经过一番挖掘,并感谢评论者,阿里Mirzaei,帮助确定问题发生在哪里,我们发现2个问题:
1.我需要一个单独的useInView钩子来处理每个被观察的元素。
1.在组件调用中使用“ref”会产生错误:“警告:无法为函数组件提供引用。尝试访问此引用将失败。”因此,我使用https://stackoverflow.com/a/65756885/13471663的答案将引用作为名为innerRef的属性传递
工作代码如下:

const App = () => {
  const { ref, inView } = useInView();

  const { ref: ref1, inView: inView1 } = useInView();

  const { ref: ref2, inView: inView2 } = useInView();

  const { ref: ref3, inView: inView3 } = useInView();

  const { ref: ref4, inView: inView4 } = useInView();

  const [activeElement, setActiveElement] = useState('#')

  useEffect(() => {
    if (inView) {
      setActiveElement('#home');
      console.log('home');
    };
    if (inView1) {
      setActiveElement('#about')
      console.log('about');
    };
    if (inView2) {
      setActiveElement('#experience')
      console.log('experience');
    };
    if (inView3) {
      setActiveElement('#services')
      console.log('services');
    };
    if (inView4) {
      setActiveElement('#contact')
      console.log('contact');
    };
  }, [inView, inView1, inView2, inView3, inView4])
  
  return (
    <>
      <Header innerRef={ref} />
      <Nav activeElement={activeElement}/>
      <About innerRef={ref1} />
      <p>{activeElement} {inView.toString()}</p>
      <Experience innerRef={ref2} />
      <Services innerRef={ref3} />
      <Contact innerRef={ref4} />
      <Footer />
      <p>{activeElement}</p>
    </>
  )
}

export default App

对于导航组件:

const Nav = ({activeElement}) => {
  const [activeNav, setActiveNav] = useState('#home');
  
  useEffect(() => {
    setActiveNav(activeElement);
  })

  return (
    <nav>
      <a href="#" onClick={() => setActiveNav('#home')} className={activeNav === '#home' ? 'active' : ''}><AiOutlineHome /></a>
      <a href="#about" onClick={() => setActiveNav('#about')} className={activeNav === '#about' ? 'active' : ''}><AiOutlineUser /></a>
      <a href="#experience" onClick={() => setActiveNav('#experience')} className={activeNav === '#experience' ? 'active' : ''}><HiOutlineBookOpen /></a>
      <a href="#services" onClick={() => setActiveNav('#services')} className={activeNav === '#services' ? 'active' : ''}><FaUncharted /></a>
      <a href="#contact" onClick={() => setActiveNav('#contact')} className={activeNav === '#contact' ? 'active' : ''}><RiMessage2Line /></a>
    </nav>
  )
}

export default Nav

下面是从组件使用innerRef的示例:

const About = ({ innerRef }) => {
  return (
    <section id='about'>
      <div ref={innerRef}>
        About
      </div>
    </section>
  )
}

希望这对任何遇到同样问题的人都有帮助!

xghobddn

xghobddn2#

首先,setActiveElement={inView ? '#home' : '' }在您的组件中是什么?
如果要跟踪的每个组件在视口中,则必须为其传递不同的引用,以便形成react-intersection-observer文档:

import * as React from "react";
// @ts-ignore Wrong type
import { createRoot } from "react-dom/client";
import { useInView } from "react-intersection-observer";
import ScrollWrapper from "./elements/ScrollWrapper";
import "./styles.css";

function App() {

  const { ref, inView } = useInView({
    threshold: 0
  });

  const { ref: ref2, inView: inView2 } = useInView({
    threshold: 0
  });

  return (
    <ScrollWrapper inView={inView}>
      <div ref={ref} className="inview-block">
        <h2>
          Element is inside the viewport: <strong>{inView.toString()}</strong>
        </h2>
      </div>
      <div ref={ref2} className="inview-block">
        <h2>
          Element is inside the viewport: <strong>{inView2.toString()}</strong>
        </h2>
      </div>
    </ScrollWrapper>
  );
}

const root = createRoot(document.getElementById("root"));
root.render(<App />);
mi7gmzs6

mi7gmzs63#

解决方案+动画滚动React+ typescript :

我做了和@BettyWhite相同的事情,但是当交集观察器处于活动状态时,我还为每个组件添加了一个动画。因此,当用户滚动时,检测到的组件会以自定义动画显示。
App.tsx:

const Default: React.FC<Props> = () => {
const { theme } = useTheme();
const [activeElement, setActiveElement] = useState("#");

const options = {
  rootMargin: "-200px",
  // triggerOnce: true,
};
const { ref: refHome, inView: inViewHome } = useInView(options);
const { ref: refAbout, inView: inViewAbout } = useInView(options);
const { ref: refSkills, inView: inViewSkills } = useInView(options);
const { ref: refProjects, inView: inViewProjects } = useInView(options);
const { ref: refServices, inView: inViewServices } = useInView(options);
const { ref: refTravel, inView: inViewTravel } = useInView(options);
const { ref: refContact, inView: inViewContact } = useInView(options);

useEffect(() => {
  if (inViewHome) setActiveElement("#home");
  if (inViewAbout) setActiveElement("#about");
  if (inViewSkills) setActiveElement("#skills");
  if (inViewProjects) setActiveElement("#projects");
  if (inViewServices) setActiveElement("#services");
  if (inViewTravel) setActiveElement("#travel");
  if (inViewContact) setActiveElement("#contact");
}, [
inViewAbout,
inViewContact,
inViewHome,
inViewProjects,
inViewTravel,
inViewServices,
inViewSkills,
]);

return (
  <div id={theme}>
    <NavBar activeElement={activeElement} />
    <main>
      <div className="container">
        <div ref={refHome} className={inViewHome ? "animate" : "opacity-0"}>
        <Home />
      </div>
      <div ref={refAbout} className={inViewAbout ? "animate" : "opacity-0"}>
        <About />
      </div>
      <div
        ref={refSkills}
        className={inViewSkills ? "animate" : "opacity-0"}
      >
        <Skills />
      </div>
      <div
        ref={refProjects}
        className={inViewProjects ? "animate" : "opacity-0"}
      >
        <Projects />
      </div>
      <div
        ref={refServices}
        className={inViewServices ? "animate" : "opacity-0"}
      >
        <Services />
      </div>
      <div
        ref={refTravel}
        className={inViewTravel ? "animate" : "opacity-0"}
      >
        <Travel />
      </div>
      <div
        ref={refContact}
        className={inViewContact ? "animate" : "opacity-0"}
      >
        <Contact />
      </div>
      <Footer />
    </div>
  </main>
</div>
);
};

应用程序(样式):

.animate {
  opacity: 0;
  animation: appear 1s ease forwards;
  animation-delay: 0.1s;
}

@keyframes appear {
  0% {
    opacity: 0;
  }

  100% {
    opacity: 1;
  }
}

然后,其中的每个组件都有一个id引用,例如:

const Home: React.FC = () => {
  return (
    <Section className="home section" id="home">
      <div className="home__container grid">
    <div className="home__content grid">
      <Social />
      <div className="home__img"></div>
      <HomeData />
    </div>
    <ScrollDown />
  </div>
</Section>
);
};

其他组件也是如此,所以当交集激活时,组件会显示自定义动画。对于navBar,我使用了类似于@BettyWhite的代码。

相关问题