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},名字
)
}
当Iconsole.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])
}