ReactJS re-render问题:useEffect导致页面每4秒重新加载

ReactJS re-render问题:useEffect导致页面每4秒重新加载

问题描述

我在Footer组件中遇到了奇怪的行为。每4秒,useEffect会重新渲染整个页面和组件。它应该只更改那个元素。不确定我做错了什么。

当注释掉CookieBanner组件时,这种情况就不会发生。同时,数据的控制台日志每4秒被触发一次。

"use client"

import type { SettingsPayload } from "types"

import { useEffect, useRef, useState } from "react"

import { usePathname } from "next/navigation"

import { Button, IconsLibrary, ThemeSwitch } from "@/components/shared"
import { CookieBanner } from "@/components/shared/CookieBanner"
import { CustomPortableText } from "@/components/shared/CustomPortableText"

interface FooterProps {
  data: SettingsPayload
}

const footerData = {
  currentProjects: {
    amount: 4,
    projects: [
      {
        projectName: "Aantal",
        description: "lopende projecten"
      },
      {
        projectName: "Zorgwijzer",
        description: "lopende projecten"
      },
      {
        projectName: "Energievergelijk",
        description: "Dé vergelijkingssite voor creditcards"
      },
      {
        projectName: "Creditcard",
        description: "lopende projecten"
      }
    ]
  }
}

const Footer = ({ data }: FooterProps) => {
  const pathname = usePathname()

  if (!data) return null

  const [currentProject, setCurrentProject] = useState(0)

  const {
    socials,
    email,
    cookieBanner,
    phone,
    c2a_internalLink,
    page_title,
    address,
    company_details,
    marquee_text
  } = data

  const intervalRef = useRef<NodeJS.Timeout | null>(null)

  useEffect(() => {
    intervalRef.current = setInterval(() => {
      setCurrentProject((prevIndex) => (prevIndex + 1) % footerData.currentProjects.projects.length)
    }, 4000)

    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current)
      }
    }
  }, [])

  const navArray = Array.from(
    { length: footerData.currentProjects.projects.length },
    (_, index) => {
      // Create your element here based on the index or originalArray
      return `Element {index}`
    }
  )

  const Socials = () => {
    return (
      socials &&
      socials.length && (
        <div className="mb-[40px] grid grid-cols-2 gap-[4px] md:mb-0 xl:grid-cols-3">
          {socials.map((social) => {
            return (
              <Button
                variant="primary"
                className="w-full min-w-[130px] max-w-[130px] border-1 border-black bg-transparent px-6 py-[7px] text-center text-base leading-none"
                type="link-external"
                url={social.url}
                key={social.name}
              >
                {social.name}
              </Button>
            )
          })}
        </div>
      )
    )
  }

  return (
    <>
      {pathname !== "/" && data && (
        <div className="grid grid-cols-6 gap-x-[20px] bg-primary px-[10px] pb-[20px] pt-[20px] md:px-10  md:pb-[50px] lg:grid-cols-footer">
          <div className="col-span-6 flex h-[400px] flex-col items-start rounded-[25px] bg-[#40e643] px-[30px] pb-[21px] pt-[32px] md:px-[50px] md:pb-[24px] md:pt-[35px] lg:col-auto">
            {marquee_text && (
              <h2 className="mb-[40px] flex h-[44px] max-w-[244px] overflow-hidden whitespace-nowrap rounded-[100px] bg-black text-3xl text-white">
                <div className="flex animate-marquee items-center justify-between whitespace-nowrap font-medium">
                  {marquee_text}
                </div>
              </h2>
            )}

            <div>
              {email && <h3 className="text-3xl font-medium leading-tight text-black">{email}</h3>}
              {phone && <h3 className="text-3xl font-medium leading-tight text-black">{phone}</h3>}
            </div>

            {c2a_internalLink && (
              <Button
                variant="primary"
                className="mt-[32px] flex items-center justify-between gap-x-4 rounded-[12px] border-2 border-black bg-transparent font-medium"
                iconType="sm-arrow-right"
                iconClassName="ml-0"
                type="link-internal"
                url={c2a_internalLink.slug}
              >
                {page_title ? page_title : c2a_internalLink.title}
              </Button>
            )}

            <div className="mt-[70px] flex w-full justify-between text-[14px] text-black">
              <span>BrightBunch</span>
              <span>© 2015 - 2023</span>
            </div>
          </div>
          <div className="col-span-6 mt-[10px] flex flex-col items-start justify-between rounded-[25px] bg-[#a5a5a5] px-[30px] py-[30px] md:col-span-4 md:px-[50px] md:pb-[30px] md:pt-[40px] lg:col-auto lg:mt-0">
            <Socials />

            <div className="flex w-full flex-col justify-between gap-[16px] text-[14px] text-black md:flex-row">
              {address && (
                <div className="flex flex-col">
                  <CustomPortableText value={address} />
                </div>
              )}

              {company_details && (
                <div className="flex flex-col">
                  <CustomPortableText value={company_details} />
                </div>
              )}
            </div>
          </div>
          <div className="col-span-6 mt-[10px] flex flex-row gap-[10px] md:col-span-2 md:flex-col md:gap-[20px] lg:col-auto lg:mt-0">
            <div className="flex h-full w-1/2 items-center justify-center rounded-[25px] bg-[#313131] md:h-[100px] md:w-full">
              <ThemeSwitch
                wrapperClass="flex h-full w-full"
                buttonClass="w-full h-full flex justify-center place-content-center items-center"
              />
            </div>
            <div className="flex grow flex-col items-center justify-between rounded-[25px] bg-[#313131] p-[15px]">
              <h2 className="mt-[16px] font-base text-[42px] text-white md:mt-[20px] md:text-[62px]">
                {footerData.currentProjects.amount}
              </h2>
              <div className="relative mt-[17px] h-[60px] w-full text-center md:mt-[20px]">
                {footerData.currentProjects.projects.map((project, index) => {
                  return (
                    <div
                      key={project.projectName}
                      className={`absolute left-0 top-0 h-full w-full text-2xl leading-tight transition-all duration-2000   ease-in-out{
                        index === currentProject ? "opacity-100" : "pointer-events-none opacity-0"
                      }`}
                    >
                      {currentProject === index ? (
                        <div className="text-[14px] text-white">
                          <h3 className="text-[14px] text-[#a5a5a5]">{project.projectName}</h3>
                          <span>{project.description}</span>
                        </div>
                      ) : (
                        <></>
                      )}
                    </div>
                  )
                })}
              </div>

              <div className="mt-2 flex">
                {navArray.map((el, index) => {
                  return (
                    <div
                      key={el}
                      className={`mr-[8px] h-[6px] rounded-[100px]  transition-all  duration-1000 ease-in-out hover:cursor-pointer ${
                        index === currentProject ? "w-[20px] bg-white" : "w-[6px] bg-[#737373]"
                      }`}
                      onClick={() => setCurrentProject(index)}
                    />
                  )
                })}
              </div>
            </div>
          </div>
        </div>
      )}

      <CookieBanner data={cookieBanner} />
    </>
  )
}

export default Footer

CookieBanner.tsx:

"use client"

import Link from "next/link"

import CookieConsent from "react-cookie-consent"

type CookieBannerProps = {
  data?: CookieBanner
}

type CookieBannerButtonProps = {
  children: React.ReactNode
  onClick: () => void
}

export async function CookieBanner({ data }: CookieBannerProps) {
  if (!data || !data.enabled) {
    return null
  }

  const { title, buttonText, privacy_link_text, privacy_url } = data

  const debug = process.env.NODE_ENV === "development"

  const ButtonComponent = ({ children, onClick }: CookieBannerButtonProps) => {
    return (
      <div className="flex items-center gap-5">
        {privacy_url && (
          <Link
            href={`/${privacy_url}`}
            className="flex items-center gap-2 text-sm text-white lg:text-base  "
          >
            {privacy_link_text ? privacy_link_text : "Privacy Policy"}
          </Link>
        )}
        <button
          onClick={onClick}
          className="inline-flex items-center text-sm text-white lg:text-base"
        >
          <span className="pl-3">{children}</span>
        </button>
      </div>
    )
  }

  return (
    <CookieConsent
      disableStyles
      location="bottom"
      buttonText={buttonText ? buttonText : "Accepteren"}
      cookieName="CookieConsent"
      expires={365}
      debug={debug}
      containerClasses="bg-black text-white justify-between flex flex-col sm:flex-row px-6 py-4 w-full sm:items-center fixed gap-3 bottom-0 left-0 w-full z-40"
      ButtonComponent={ButtonComponent}
    >
      <h3 className="text-lg sm:text-xl">{title}</h3>
    </CookieConsent>
  )
}

解决方案

如果您只想更新组件中的特定元素,而不会导致整个组件或页面重新渲染,您可以使用React的useMemouseCallback hooks来记忆您想要更新的组件部分。这可以帮助优化您的组件并防止不必要的重新渲染。

以下是您可以修改组件以实现此目的的方法:

import React, { useState, useEffect, useCallback } from 'react';

function YourComponent() {
  const [currentProject, setCurrentProject] = useState(0);
  const projects = footerData.currentProjects.projects;

  // Memoize the component rendering this specific element
  const projectElement = useCallback(() => {
    return (
      <div>
        {/* Your element that you want to update */}
        {projects[currentProject]}
      </div>
    );
  }, [currentProject, projects]);

  const intervalRef = useRef<NodeJS.Timeout | null>(null);

  useEffect(() => {
    intervalRef.current = setInterval(() => {
      setCurrentProject((prevIndex) => (prevIndex + 1) % projects.length);
    }, 4000);

    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    }
  }, [projects]);

  return (
    <div>
      {/* Render the memoized element */}
      {projectElement()}
    </div>
  );
}

export default YourComponent;

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程