useEffect 依赖数组中的 window.innerwidth 未调用回调函数

window.innerwidth in useEffect dependency array is not calling the callback function

提问人:Finlay Foulds 提问时间:11/17/2023 更新时间:11/17/2023 访问量:18

问:

该组件应该在初始渲染时在 h2 元素中显示日期、日期和月份。函数内部的 useEffect 旨在在 window.innerWidth < 735px 时将日期更改为短格式,并在非 735px 时将日期更改为长格式。它还旨在在日期更改时重新呈现组件。useEffect 回调函数在第一次渲染时被调用,并根据日期和 innerWidth 完全按预期显示日期,但当日期或 innerWidth 更改时不会再次显示,尽管 day、date、month 和 innerWidth 都包含在依赖数组中,使用 setInterval 函数每 2 秒检查一次日期

我的代码:

import { useState, useEffect } from "react"

let formattedDate
let currentDate = new Date

export default function LocalDate() {

  const [date, setDate] = useState("")
  
  useEffect(() => {
    const interval = setInterval(() => {
      currentDate = new Date
    }, 2000)
    console.log("called")
    if (window.innerWidth < 735) {
      formattedDate = currentDate.toLocaleDateString("en-US", { weekday: "short", day: "numeric", month: "short" })
    } else {
      formattedDate = currentDate.toLocaleDateString("en-US", { weekday: "long", day: "numeric", month: "long" })
    }

    let parts = formattedDate.split(" ")
    let formattedDateSwapped = `${parts[0]} ${parts[2]} ${parts[1]}`
    setDate(formattedDateSwapped)

    return clearInterval(interval)
  }, [currentDate.getDay(), currentDate.getDate(), currentDate.getMonth(), window.innerWidth])

  return (
    <h2 id="date">{date}</h2>
  )
}

我已经看过无数次了,用谷歌搜索了类似的问题,在许多论坛上都找不到答案。如果有人对我的问题有任何建议或解决方案,将不胜感激。请记住,我大约一周前才开始学习反应。谢谢

reactjs date react-hooks 重新渲染

评论


答:

0赞 Damià 11/17/2023 #1

我认为您需要使用状态对象来允许useEffect检测更改。 例如,如果创建此状态:

const [currentWidth, setCurrentWidth] = useState(0);

然后,您可以使用间隔每 x 秒填充一次此状态:

const interval = setInterval(() => {
  setCurrentWidth(window.innerWidth);
}, 2000)

现在,您可以在 useEffect 控件中使用它:

useEffect(() => { /*do stuff*/ }, [currentWidth])

我希望这能帮到你。

0赞 Harrison 11/17/2023 #2

这不起作用的原因是窗口宽度的更改(特别是 )不会在依赖项数组中创建更改。在监听窗口时,我发现简单地添加和删除事件侦听器(例如 /)是很好的方法......windowuseEffectaddEventListenerremoveEventListener

您不依赖于状态来侦听和更改页面宽度的定义值。
当组件更改或卸载时,能够控制它们(即清除它们)也是一种很好的做法(在使用超时和间隔时)(这样您就不会发生内存泄漏)。
习惯于这样(以及下面)定义它们,因为它非常明确地说明您在超时时做什么。使用 s 也不会在更改值时导致重新呈现。
React Docs 在“何时使用 refs”中也提到了这一点。
reftimeout.current

const { useState, useEffect, useRef } = React

const shortDate = { weekday: "short", day: "numeric", month: "short" }
const longDate = { weekday: "long", day: "numeric", month: "long" }


const formatDate = () => {
  const currentDate = new Date();
  return window.innerWidth < 735
    ? currentDate.toLocaleDateString("en-US", shortDate)
    : currentDate.toLocaleDateString("en-US", longDate);
}

function LocalDate () {

  const [formattedDate, setFormattedDate] = useState(formatDate());
  const intervalRef = useRef(null)
  
  useEffect(() => {
    const getNewDate = () => {
        setFormattedDate(formatDate());
        console.log("called");
      }
    
    const getNewDateInterval = () => {
      if (intervalRef.current) {
        console.log("cleared 1")
        clearInterval(intervalRef.current)
      }
      intervalRef.current = setInterval(getNewDate, 2000)
    }
    
    // call once to start off
    getNewDateInterval()
    // add listener
    window.addEventListener("resize", getNewDate);

    // clean up
    return () => {
      if (intervalRef.current) {
        console.log("cleared 2")
        clearInterval(intervalRef.current)
      }
      window.removeEventListener("resize", getNewDate);
    }
  }, [])

  return (
    <h2 id="date">{formattedDate}</h2>
  )
}

// Render it
ReactDOM.createRoot(
    document.getElementById("root")
).render(
    <LocalDate />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>