reactjs 我想在react中更改url而不重绘屏幕

sd2nnvve  于 2023-03-01  发布在  React
关注(0)|答案(1)|浏览(105)

我想滚动到下一个视频,比如YouTube短视频或者TikTok视频,滚动的时候想把URL改成当前显示视频的id,比如"https://example.com/{id}"
我可以用下面的代码实现它,但是视频变白了一会儿,可能是因为URL被navigate()更改了。
我该怎么补救呢?

interface Video {
  id: string;
  videoUrl: string;
}

function Videos() {
  const [videos, setVideos] = useState<Video[]>([]);
  const params = useParams();
  const navigate = useNavigate();
  const refs = useRef<RefObject<HTMLDivElement>[]>([]);

  const observer = new IntersectionObserver(entries => {
    entries.forEach((entry) => {
      const video = entry.target.querySelector("video") as HTMLVideoElement;

      if (entry.isIntersecting) {
        if (params.id !== id) navigate(id);
        video.play();
      } else {
        video.pause();
      }
    });
  }, { threshold: 0.5 });

  useEffect(() => {
    refs.current.forEach((ref) => {
      if (ref.current) {
        observer.observe(ref.current)
      }
    })

    return () => {
      refs.current.forEach((ref) => {
        if (ref.current) {
          observer.unobserve(ref.current)
        }
      })
    }
  });

  const fetchVideos = async (): Promise<Video[]> => {
    // ...
  };

  useEffect(() => {
    fetchVideos().then(videos => {
      videos.forEach((v, i) => refs.current[i] = createRef<HTMLDivElement>());
      setVideos(videos);
      if (!params.id) navigate(contents[0].id);
    }).catch(error => {
      alert("error");
    });
  }, []);
  
  const Content = forwardRef(({ video }: { video: Video }, ref) => {
    const videoRef = ref as MutableRefObject<HTMLDivElement>;

    return (
      <>
        <div ref={videoRef}>
          <video src={video.videoUrl} playsInline muted></video>
          <input type="hidden" name="contentId" value={video.id} />
        </div>
      </>
    );
  });

  return (
    <div className="Videos">
      {videos.map((v, i) => <Content key={v.id} ref={refs.current[i]} video={v} />)}
    </div>
  );
}

export default Videos;

CSS

.Videos {
    scroll-snap-type: y mandatory;
    overflow: auto;
    height: 100vh;

    > * {
        scroll-snap-align: start;
    }
}

video {
    height: 100vh;
}
rm5edbpk

rm5edbpk1#

我看到的代码中唯一明显的问题是Content组件是在另一个React组件中声明的。每次Videos组件重新呈现时,无论出于什么原因,Content组件都会重新声明。这意味着Content组件的任何"示例"都将被卸载,并安装新的"示例"。
单独声明React组件,使其成为稳定引用。

const Content = forwardRef(({ video }: { video: Video }, ref) => {
  const videoRef = ref as MutableRefObject<HTMLDivElement>;

  return (
    <div ref={videoRef}>
      <video src={video.videoUrl} playsInline muted />
      <input type="hidden" name="contentId" value={video.id} />
    </div>
  );
});
function Videos() {
  const [videos, setVideos] = useState<Video[]>([]);
  const params = useParams();
  const navigate = useNavigate();
  const refs = useRef<RefObject<HTMLDivElement>[]>([]);

  ...

  return (
    <div className="Videos">
      {videos.map((v, i) => (
        <Content key={v.id} ref={refs.current[i]} video={v} />
      ))}
    </div>
  );
}

相关问题