目錄
一.思考
二. 舉例
三.驗(yàn)證
四.小結(jié)
一.首先不太了解的先看官方文檔 本文的目的逻谦,是想通過一個(gè)簡單的例子詳細(xì)分析一些令人疑惑的問題及其背后的原因舵变。
思考: 先來看個(gè)例子
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
// console.log(count);
setCount(count + 1);
}, 1000);
}, []);
return <h1>{count}</h1>;
}
我們期望袱讹,useEffect只執(zhí)行一次面睛,且后續(xù)每隔1s,count自動(dòng)+1,然而冲甘,實(shí)際上count從0到1后绩卤,再?zèng)]有變化,一直都是1江醇。
難道setInterval沒有執(zhí)行濒憋,于是我們帶著疑惑加上了console.log
事實(shí)是,setInterval每次執(zhí)行的時(shí)候嫁审,拿到的count都得0,很自然的我們會(huì)想到閉包赖晶,但是閉包能完全解釋這個(gè)現(xiàn)象嗎律适?
我們稍加修改再看下這個(gè)例子:
function Counter() {
const [count, setCount] = useState(0);
let num = 0;
useEffect(() => {
const id = setInterval(() => {
// 通過 num 來給 count 提供值
console.log(num);
setCount(++num);
}, 1000);
}, []);
return <h1>{count}</h1>;
}
我們可以看到,借助num 這個(gè)中間變量遏插,我們可以得到想要的結(jié)果捂贿。但是,同樣是閉包胳嘲,為什么num就能記住之前的的值呢厂僧?
其實(shí)問題出在count上,繼續(xù)往下看:
function Counter() {
// ...
console.log('我是 num', num);
return <h1>{count}-----{num}</h1>;
}
渲染的num和定時(shí)器中的 num 為什么不一樣呢了牛?
每次都是重新執(zhí)行
到這里我想說的到底是什么呢颜屠?
我們可以清晰的看到渲染出的num和setinterval中的num,是不同的鹰祸。
這是因?yàn)樵赗eact中甫窟,對(duì)于函數(shù)式組件來講,每次更新都會(huì)重新執(zhí)行一次函數(shù)蛙婴。也就是說粗井,每次更新都會(huì)在當(dāng)前作用域重新聲明一個(gè) let num=0
,所以街图,定時(shí)器中閉包引用的那個(gè)num,和每次更新時(shí)渲染的num,根本不是同一個(gè)浇衬。當(dāng)然,我們能很輕易地把他們變成同一個(gè)餐济。
let num = 0; // 將聲明放到渲染組件外面
function Counter() {
// ...
return <h1>{count}-----{num}</h1>;
}
嗯耘擂,說了這么多,跟count 有什么關(guān)系呢絮姆?
同理梳星,正因?yàn)楹瘮?shù)組件每次都會(huì)整體重新執(zhí)行赞赖,那么HOOKS當(dāng)然也是這樣。
function Counter() {
const [count, setCount] = useState(0);
// ...
}
useState應(yīng)該理解為和普通的javascript函數(shù)一樣冤灾,而不是React的什么黑魔法函數(shù)組件更新的時(shí)候前域,useState會(huì)重新執(zhí)行,對(duì)應(yīng)的韵吨,也會(huì)重新聲明[count, setCount]這一組常量匿垄。只不過React對(duì)這個(gè)函數(shù)做了一些特殊處理,比如:
首次執(zhí)行時(shí)归粉,會(huì)將useState的參數(shù)初始化給count椿疗,而以后在執(zhí)行時(shí),則會(huì)直接取上次setCount(如果有調(diào)用)賦過的值(React通過某種方式保存起來的)糠悼。
有了這個(gè)概念届榄,就不難知道,定時(shí)器里的setCount(count + 1)
倔喂,這個(gè) count 和每次更新重新聲明的 count铝条,也是完全不同的兩個(gè)常量,只不過它們的值席噩,可能會(huì)相等班缰。
比如,我們嘗試把之前的 num悼枢,直接用 count 替代埠忘。
function Counter() {
// 注意這里變成 let
let [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
// 這種寫法是不好的
setCount(++count);
}, 1000);
}, []);
console.log(count);
return <h1>{count}</h1>;
}
這時(shí)候不論是打印還是頁面表現(xiàn)都和你期望的一樣。
但這違背了React的原則馒索,而且也讓程序變得更加讓人迷惑莹妒。
也就導(dǎo)致了你并不能清楚的知道:此時(shí)渲染的count和set interval中的count已經(jīng)不是同一個(gè)了。盡管他們的值是相等的绰上。
當(dāng)然动羽,這種場景下react提供了可行的方法,能夠每次拿到count的最新值渔期,就是給setCount傳遞一個(gè)回調(diào)函數(shù)运吓。
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
// 注意:這里變成回調(diào)了
setCount(count => count + 1);
}, 1000);
}, []);
return <h1>{count}</h1>;
}
執(zhí)行圖解
回過頭再看看開始的例子:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
}, 1000);
}, []);
return <h1>{count}</h1>;
}
小結(jié):
count 每次都被重新聲明了,setInterval 因?yàn)?useEffect 設(shè)置了只執(zhí)行一次的緣故疯趟,在第一次更新時(shí)閉包引用的 count 始終是 0拘哨,后續(xù)更新的 count 和它沒關(guān)系。