Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sqlite/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 了解反作用效应&;最佳做法_Javascript_Reactjs_React Hooks - Fatal编程技术网

Javascript 了解反作用效应&;最佳做法

Javascript 了解反作用效应&;最佳做法,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,我有一个个人问候组件,其设置如下: constPersonalGreeting=():ReactElement=>{ 常量[timeOfDay,setTimeOfDay]=useState(null) const[date,setDate]=useState(新日期()) useffect(()=>{ 常量计时器=设置间隔(()=>{ 设置日期(新日期()) }, 1000) setTimeOfDay(getTimeOfDay(日期)) return()=>{ 清除间隔(计时器) } },[日期

我有一个个人问候组件,其设置如下:

constPersonalGreeting=():ReactElement=>{
常量[timeOfDay,setTimeOfDay]=useState(null)
const[date,setDate]=useState(新日期())
useffect(()=>{
常量计时器=设置间隔(()=>{
设置日期(新日期())
}, 1000)
setTimeOfDay(getTimeOfDay(日期))
return()=>{
清除间隔(计时器)
}
},[日期])
返回(
好{timeOfDay},名字
)
}
当I
console.log
useffect
函数之外时,它似乎运行了两次。在它曾经运行过的
useffect
函数中,我想知道是我设置的不正确,还是有更好的方法

辅助函数-

const getTimeOfDay=(日期:date):字符串=>{
让timeOfDay=“”
const hour=date.getHours()
const minutes=date.getMinutes()
常数等时锁=分钟===0
如果((小时>=4&&hour<12)| |(小时==12&&isOClock)){
timeOfDay=‘早晨’
}
如果((小时>=12&&!等时锁&&hour<17)| |(小时===17&&isOClock)){
timeOfDay=‘下午’
}
如果((小时>=17&&!等时锁)| |小时<4){
timeOfDay=‘晚上’
}
返回时间
}

您提到了最佳实践,所以我只介绍一下我遇到的问题。实际上这里有很多很酷的东西

正如其他人所提到的,
useffect
在每次渲染后运行,包括第一次渲染。它有效地实现了
componentDidMount
componentdiddupdate

渲染运行两次,因为第一次渲染后,
useffect
正在更改react状态

  • 第一次渲染->设置效果
  • 效果随后立即触发->更改<代码>日期时间
  • React运行再次渲染以处理新状态
    timeOfDay
最简单的修复方法是在第一次渲染期间将第一个
timeOfDay
设置为默认值。这样,它就不必在效果期间进行更改

const[date,setDate]=useState(new date())
常量[timeOfDay,setTimeOfDay]=useState(getTimeOfDay(日期))
然后将代码移动到间隔内设置
timeOfDay
,使用
setDate

实际上,您可以做得更好,因为只需将一天中的时间置于react状态,您就违反了最小基本状态原则,这在声明式编程中相当重要。(注意:我刚刚编造了这个术语。我认为还没有一个好的术语来描述它。)我的意思是,
timeOfDay
完全来自于
date
,它已经处于react状态,所以没有理由(除了性能问题)将两者都保持在react状态。任何时候发生这种情况,你都应该考虑消除冗余状态。在这种情况下,这将涉及删除
timeOfDay
状态并仅写入

    <>
    Good {getTimeOfDay(date)}, Name
    </>
看起来很像,对吧?它基本上是相同的,但它只在渲染期间运行,因此不会导致出现双渲染问题。具体来说,
usemo
将在第一次渲染期间运行
getTimeOfDay
,但只有在
[date]
发生更改时,才会在任何后续渲染中重新运行
getTimeOfDay
;否则,它将返回以前使用的
timeOfDay

最后一件事值得一提。您将
[date]
传递到
useffect
中,但这实际上是错误的。它会工作,但不是预期的用例。该数组用于告诉react效果函数从读取时处于什么状态,以引起其副作用,但您不是从日期读取,因此当日期更改时,不需要重新运行效果。您真正想要的是传入
[]
-无状态。这样react就知道副作用不依赖于任何东西,并且在第一次渲染期间只应运行一次。现在,使用间隔而不是超时是毫无意义的,因为react正在取消并重新建立每个渲染的间隔

在我描述的所有更改中,您的组件看起来应该是这样的

const PersonalGreeting = (): ReactElement => {
  const [date, setDate] = useState(new Date())

  useEffect(() => {
    const timer = setInterval(() => {
      setDate(new Date())
    }, 1000)

    return () => {
      clearInterval(timer)
    }
  }, [])

  return (
    `Good ${getTimeOfDay(date)}, Name`
  )
}
我还切换到字符串模板,我认为这更好,但这更多的是一种观点。

最终版本:

const PersonalGreeting = (): ReactElement => {
  const [timeOfDay, setTimeOfDay] = useState(getTimeOfDay(new Date()))

  useEffect(() => {
    const timer = setInterval(() => {
      setTimeOfDay(getTimeOfDay(new Date()))
    }, 60000)

    return () => {
      clearInterval(timer)
    }
  }, [])

  return useMemo(() => {
    return (
      <PersonalGreetingContainer>
        <PersonalGreetingHeading weight="light">
          Good {timeOfDay},{' '}
          {givenName}
        </PersonalGreetingHeading>
      </PersonalGreetingContainer>
    )
  }, [givenName, timeOfDay])
}
constPersonalGreeting=():ReactElement=>{
const[timeOfDay,setTimeOfDay]=useState(getTimeOfDay(new Date()))
useffect(()=>{
常量计时器=设置间隔(()=>{
setTimeOfDay(getTimeOfDay(新日期()))
}, 60000)
return()=>{
清除间隔(计时器)
}
}, [])
返回UseMoom(()=>{
返回(
好{timeOfDay},{'}
{givename}
)
},[givenName,timeOfDay])
}

Log two-external effect(外部效应)与功能组件主体一样,通常意味着您正在将应用程序呈现到一个
StrictMode
react组件中,该组件将呈现两次以检测无意的副作用。此外,间隔回调更新了
日期
状态,再次触发效果回调,该回调会删除上一个间隔并创建一个新的间隔,这可能完全没有必要。我不确定你在这里的目标是什么,只是为了提出一个好的建议。这不是也因为useEffect在第一次渲染之后运行吗?因此,在fn主体中记录一次日志,执行useffect,触发重新渲染器,并记录两次。在useEffect内部,它只记录第一次,因此我看不到此处使用了
useMemo()
,您是否建议仍使用此功能?此功能不适用。目前,组件仅在
date
更改时呈现,因此每次都必须重新计算
getTimeOfDay
<代码>使用备忘录实际上不会减少任何计算<如果您有其他一些不相关的状态,例如const PersonalGreeting = (): ReactElement => { const [timeOfDay, setTimeOfDay] = useState(getTimeOfDay(new Date())) useEffect(() => { const timer = setInterval(() => { setTimeOfDay(getTimeOfDay(new Date())) }, 60000) return () => { clearInterval(timer) } }, []) return useMemo(() => { return ( <PersonalGreetingContainer> <PersonalGreetingHeading weight="light"> Good {timeOfDay},{' '} {givenName} </PersonalGreetingHeading> </PersonalGreetingContainer> ) }, [givenName, timeOfDay]) }