英文:
Intersection observer only works with the last element in Next.js
问题
const AboutMe: NextPage = ({ data }: any) => {
const targets = useRef<HTMLDivElement[]>(new Array(data.results.length).fill(null));
useEffect(() => {
let observer: IntersectionObserver;
const handleIntersection = (entries: IntersectionObserverEntry[]) => {
entries.forEach((entry, index) => {
const targetElement = entry.target as HTMLElement;
if (entry.isIntersecting) {
targetElement.style.opacity = "1";
} else {
targetElement.style.opacity = "0";
}
});
};
if (targets.current.every((el) => el !== null)) {
observer = new IntersectionObserver(handleIntersection, { threshold: 0.5 });
targets.current.forEach((target) => observer.observe(target as Element));
}
return () => {
if (observer) {
observer.disconnect();
}
};
}, [targets, data.results]);
return (
<main>
<section className="container px-5 py-24 mx-auto space-y-20">
{data.results?.map((item: any, index: number) => (
<div
className="opacity-0 transition-all duration-500"
key={item.id}
ref={(el) => (targets.current[index] = el)}
>
<ol>
<li>{item.properties.content.rich_text[0].plain_text}</li>
</ol>
</div>
))}
</section>
</main>
);
};
英文:
Intersection observer only works with the last element in Next.js
I want to target all the repeated div elements and put an opacity effect(0 or 1) every time I see them on the viewport, but only the last div element ends up having the opacity effect.
This is my code:
const AboutMe: NextPage = ({ data }: any) => {
const target = useRef<HTMLDivElement>(null);
useEffect(() => {
let observer: IntersectionObserver;
if (!target.current) return;
if (target) {
observer = new IntersectionObserver(
([e]) => {
const target = e.target as HTMLElement;
if (e.isIntersecting) {
target.style.opacity = "1";
} else {
target.style.opacity = "0";
}
},
{ threshold: 0.5 }
);
observer.observe(target.current as Element);
}
}, [target]);
return (
<main>
<section className="container px-5 py-24 mx-auto space-y-20">
{data.results?.map((item: any) => {
return (
<div
className="opacity-0 transition-all duration-500"
key={item.id}
ref={target}
>
<ol>
<li>
{
item.properties.content.rich_text[0]
.plain_text
}
</li>
</ol>
</div>
);
})}
</section>
</main>
);
};
export default AboutMe;
export async function getStaticProps() {
const options = {
...
};
const res = await fetch(
`https://api...`,
options
);
const data = await res.json();
return {
props: { data },
};
}
How can I solve this?
答案1
得分: 1
以下是翻译好的部分:
-
你的代码存在一些问题。
-
你正在多个 HTML 元素上重复使用相同的 ref 对象。这样是行不通的。你需要将每个元素的 ref 存储在其自己的 ref 对象中。
-
React 效果在 ref 对象发生变化时不会运行。你当前的效果只在组件挂载时运行一次。
为了保持代码的清晰性,你可以将出现/消失的 div 重构为一个单独的组件,该组件具有自己的 IntersectionObserver
。
import { NextPage } from "next";
import { useEffect, useRef, useState } from "react";
function Item({ item }: { item: any }) {
const ref = useRef<HTMLDivElement | null>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(({ target, isIntersecting }) => {
if (target === ref.current) {
setVisible(isIntersecting);
}
});
},
{
threshold: 0.5,
}
);
if (ref.current) {
observer.observe(ref.current);
}
return () => {
observer.disconnect();
};
}, []);
return (
<div
ref={ref}
className={`transition-all duration-500 ${
visible ? "opacity-100" : "opacity-0"
}`}
>
<ol>
<li>{item.properties.content.rich_text[0].plain_text}</li>
</ol>
</div>
);
}
const AboutMe: NextPage = ({ data }: any) => {
return (
<main>
<section className="container px-5 py-24 mx-auto space-y-20">
{data.results?.map((item: any) => (
<Item key={item.id} item={item} />
))}
</section>
</main>
);
};
export default AboutMe;
另外,请考虑用具体的类型定义替换所有的 any
类型,因为在项目中使用 any
类型会破坏使用 TypeScript 的目的。
英文:
There are several issues with your code.
-
You are re-using the same ref object for multiple HTML elements. This is not going to work this way. You need to store each element's ref in its own ref object.
-
React effects do not run when ref object changes. Your current effect just happens to run once the component mounts.
To keep the implementation clean, you can refactor your appearing/disappearing divs into a separate component with its own IntersectionObserver
.
import { NextPage } from "next";
import { useEffect, useRef, useState } from "react";
function Item({ item }: { item: any }) {
const ref = useRef<HTMLDivElement | null>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(({ target, isIntersecting }) => {
if (target === ref.current) {
setVisible(isIntersecting);
}
});
},
{
threshold: 0.5,
}
);
if (ref.current) {
observer.observe(ref.current);
}
return () => {
observer.disconnect();
};
}, []);
return (
<div
ref={ref}
className={`transition-all duration-500 ${
visible ? "opacity-100" : "opacity-0"
}`}
>
<ol>
<li>{item.properties.content.rich_text[0].plain_text}</li>
</ol>
</div>
);
}
const AboutMe: NextPage = ({ data }: any) => {
return (
<main>
<section className="container px-5 py-24 mx-auto space-y-20">
{data.results?.map((item: any) => (
<Item key={item.id} item={item} />
))}
</section>
</main>
);
};
export default AboutMe;
p.s. Please also consider replacing all any
hacks with specific type definitions, as using any
defeats the purpose of using TypeScript in a project.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论