为什么我的 nextjs 组件渲染两次?

Die*_*ero 36 reactjs firebase-storage next.js use-effect use-state

这是一个从 Firebase 存储呈现数据并将其列出的组件。该函数要做的就是将从 firebase 存储中提取的视频设置为 useState。这样我就可以调用视频并映射到一个新组件中,该组件恰好是一个按钮列表。它工作得相对很好,问题是该组件渲染了两次,第一次它不保存状态中的视频,第二次它这样做。换句话说,该组件不会等待视频保存在状态中,而只是渲染自身,导致带有视频标题的按钮列表不显示。

// ReactJS
import { useState, useEffect } from "react";

// NextJS
import { useRouter } from "next/router";

// Seo
import Seo from "../../../components/Seo";

// Hooks
import { withProtected } from "../../../hook/route";

// Components
import DashboardLayout from "../../../layouts/Dashboard";

// Firebase
import { getDownloadURL, getMetadata, listAll, ref } from "firebase/storage";
import { storage } from "../../../config/firebase";

// Utils
import capitalize from "../../../utils/capitalize";
import { PlayIcon } from "@heroicons/react/outline";

function Video() {
  // States
  const [videos, setVideos] = useState([]);
  const [videoActive, setVideoActive] = useState(null);

  // Routing
  const router = useRouter();
  const { id } = router.query;

  // Reference
  const reference = ref(storage, `training/${id}`);

  // Check if path is empty

  function getVideos() {
    let items = [];
    listAll(reference).then((res) => {
      res.items.forEach(async (item) => {
        getDownloadURL(ref(storage, `training/${id}/${item.name}`)).then(
          (url) => {
            items.push({
              name: item.name.replace("-", " "),
              href: item.name,
              url,
            });
          }
        );
      });
    });
    setVideos(items);
  }

  useEffect(() => {
    getVideos();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [id]);

  console.log(videos);

  return (
    <>
      <Seo
        title={`${capitalize(id)} Training - Dashboard`}
        description={`${capitalize(
          id
        )} training for all Every Benefits Agents.`}
      />
      <DashboardLayout>
        <h2>{capitalize(reference.name)}</h2>
        <section>
          <video controls controlsList="nodownload">
            {videoActive && <source src={videoActive} type="video/mp4" />}
          </video>
          <ul role="list" className="divide-y divide-gray-200 my-4">
            {videos.map((video, index) => (
              <button key={index} className="py-4 flex">
                <div className="w-full ml-3 flex flex-row justify-start items-center space-x-3">
                  <PlayIcon className="w-6 h-6 text-gray-600" />
                  <p className="text-sm font-medium text-gray-900">
                    {video.name}
                  </p>
                </div>
              </button>
            ))}

            {console.log("Component rendered")}
          </ul>
        </section>
      </DashboardLayout>
    </>
  );
}

export default withProtected(Video);
Run Code Online (Sandbox Code Playgroud)

这是组件如何开始渲染的示例:

在此输入图像描述

任何人都知道为什么会发生这种情况?

小智 99

我在这个线程中找到了答案。 https://github.com/vercel/next.js/issues/35822

简而言之,这个问题是由于 React 18 Strict Mode 造成的。您可以阅读React 18 严格模式中的新增功能。

如果您没有使用严格模式,则不应发生这种情况。如果不需要,可以在 next.config.js 中关闭 React Strict Mode。文件如下图所示。

const nextConfig = {
  reactStrictMode: false
}

module.exports = nextConfig
Run Code Online (Sandbox Code Playgroud)

  • 应该注意的是,这仅影响开发模式,如上面的链接所示:**“这仅适用于开发模式,生产行为不变。”** (4认同)

Mus*_*Opu 9

这是因为React 中的严格模式用于找出组件级别的常见错误,为此,React 会渲染单个组件两次。第一个渲染符合我们的预期,其他渲染用于反应严格模式。在开发阶段,您将面临这个问题,尽管这不是问题。这是为了在开发阶段获得更好的开发体验。在生产阶段默认不会出现这样的问题,没有任何错误。

我们可以从以下位置阅读更多内容:https://react.dev/reference/react/StrictMode

此外,如果这让你烦恼,你可以在 next.config.js 中禁用严格模式(我个人不喜欢禁用严格模式) :

const nextConfig = {
  reactStrictMode: false
}

module.exports = nextConfig
Run Code Online (Sandbox Code Playgroud)

谢谢你!