在開始談標題時先來寫點簡單的東西
目標是要寫一個判斷滑鼠滾動方向的元件,並將結果顯示在畫面上
於是我要先有一個紀錄位置的變數position,根據新舊position的變化來得出到底是往上滾還是往下滾
import React, { useState, useEffect } from ‘react’const Direction = () => { // save the position const [position, setPosition] = useState(0) // display direction const [direction, setDirection] = useState(‘zzz’)
const countShift = () => { const { scrollTop } = document.documentElement const oldPosition = position
setPosition(scrollTop)
// decide direction if (position > oldPosition) { setDirection(‘down’) } else if (position < oldPosition) { setDirection(‘up’) } else { setDirection(‘zzz’) } }
// listen to the scrolling event useEffect(() => { window.addEventListener(‘scroll’, countShift) return () => { window.removeEventListener(‘scroll’, countShift) } }, [position])
return ( <div style={{ height: ‘200vh’, width: ‘80vw’ }}> <div style={{ position: ‘fixed’, top: ‘50%’, left: ‘50%’ }}> {direction} </div> </div> )}export default Direction
預期的畫面應該是往下滾時畫面會有個down,往上滾時畫面會有個up

這時打開開發者工具

數字有在改變,畫面沒在更新?
這時候該來看看countShift()
這個method的邏輯看起來是
另存舊position=>更新position(更新state)=>比較新舊位置=>更新方向
實際上這裡有個重點:setState,也就是useState的第二個參數,其實是非同步執行的
所以實際上的執行順序是
另存舊position=>比較新舊位置=>更新position(更新state)=>更新方向
所以比較位置時,永遠是兩個相同的值在比較
那該怎麼改正呢?為了維持原本的函式執行順序,只要把依據比較值那一段也放進非同步的步驟就好
這時先來看看React的官方文件

所以可以這樣改
const countShift = () => { const { scrollTop } = document.documentElement setPosition((oldPosition) => { if (scrollTop > oldPosition) { setDirection(‘down’) } else if (scrollTop < oldPosition) { setDirection(‘up’) } else { setDirection(‘zzz’) } return scrollTop })}

當然你或許會發現這不是很好的例子,實際上這麼寫其實沒必要,原本的只要這樣寫就可以:
const countShift = () => { const { scrollTop } = document.documentElement // decide direction if (scrollTop — position > 0) { setDirection(‘down’) } else if (scrollTop — position < 0) { setDirection(‘up’) } else { setDirection(‘zzz’) } //renew the position setPosition(scrollTop)}
重點就是因為setState是個非同步的操作,使用時需要注意最好不要直接在setState的下面馬上去取用這個state
真的有這方面的需要的話,就要在setState裡使用arrow function來操作,使得函式能照你想要的順序執行
也就是說在setState觸發前,一個component裡的state是不會改變的
而觸發setState後,component會重新render
在每兩次render之間,state都是不會改變的
這就是React的Immutability
剛在學習react時偶爾會有遇到畫面更新問題的時候
這時不妨來想想,問題的來源是不是Immutability?
參考: