[React]The Immutability of React

張庭瑋
6 min readJun 14, 2020

--

在開始談標題時先來寫點簡單的東西

目標是要寫一個判斷滑鼠滾動方向的元件,並將結果顯示在畫面上

於是我要先有一個紀錄位置的變數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

嗯?不論怎麼滾都是代表沒移動的zzz?

這時打開開發者工具

第一個state就是我們的position

數字有在改變,畫面沒在更新?

這時候該來看看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

Sign up to discover human stories that deepen your understanding of the world.

--

--

No responses yet

Write a response