在渲染函数中像在class中一样使用state
先看一个可以暂停的时钟的例子
import React, {useEffect, useState} from "react"
//这里这样写是因为我使用了typescript,不需要的可以忽略,重点看函数里的实现
interface Interface {}
const MyTime: React.FC<Interface> = (props: Interface)=>{
// useState 可以返回一个 state,以及更新 state 的函数,名称可以自己定
// 创建一个时间的state(time),并且使用当前时间进行初始化
let [time,setTime] = useState(new Date().toLocaleString());
// 创建一个状态的state(status),初始化为true,用以停止时钟的走动
let [status,setStatus] = useState(true);
//一个定时器,定时更新时间
let timer;
useEffect(()=>{
if(status) {
timer = setTimeout(()=>{
setTime(new Date().toLocaleString())
},1000)
}
})
//定义一个函数用以停止时钟
function handler(){
let next = !status
setStatus(next)
//停止时钟时需要清除当前的定时器
if(!next){
window.clearTimeout(timer);
}
}
return <div>
<p>{time}</p>
<button onClick={handler}>pause</button>
</div>
}
export default MyTime
上面这个例子中让我们能在函数组件中使用state保存状态的就是useState这个方法,这个方法会返回我们的state和setState用以更新state,就像我们在class中使用state,在class中我们在constructor中初始化state,并且可以通过setState更新state。并且我们可以在一个函数组件中多次使用useState已返回多个state,就像上面例子中的time和status。
需要注意的是副作用函数需要放在useEffect中执行,不然可能会出现一些意想不到的bug,比如上面例子中的用于更新时间的定时器,它就需要放到useEffect中执行,不然如果你的组件使用了严格模式(被包含在StrictMode中),你会发现如果你点了暂停,这个时钟仍然会在下一次渲染后才会真正的停止,而不是立即停止。之所以会这样便是由于严格模式引起的。在严格模式下,函数组件的更新会被执行2次,导致会生成2个定时器,而我们清除定时器的时候清除的只是最近的一个定时器,导致下一个定时器仍会更新time,但是由于status已经设置为false,下次更新时就不会参数新的定时器。这样导致的现象就是下一次渲染后才会真正的暂停。
为什么严格模式下,更新会被执行2次
在react官网是这样描述StrictMode的:
StrictMode 是一个用来突出显示应用程序中潜在问题的工具,与 Fragment 一样,StrictMode 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告。
在react即将推出的concurrent 模式下,react会将渲染工作分解为多个部分,对任务进行暂停和恢复操作。这就导致了 React 在提交之前多次调用渲染阶段的一些生命周期的方法,比如constructor,componentWillMount等,这就意味者更新可能会多次执行,导致了如果在这些方法中写了副作用相关的代码可能会导致一些隐秘的bug,而严格模式却不能检查到我们写的副作用代码,所以严格模式会故意多次调用这些方法以帮助我们能发现bug。所以,上面的例子里如果你没有使用严格模式,那么即使你把定时器写到useEffect外,也能像写在useEffect中一样正常的工作。但是这是不推荐的,因为严格模式就是用来帮助我们发现这些隐秘的问题的,我们应该使用它,同时保证副作用代码是放在useEffect中的。需要注意的是StrictMode 的多次执行只是存在开发环境的,在生产环境中并不会生效。
关于严格模式更多的作用可以到react官网查看