iT邦幫忙

2026 iThome 鐵人賽

DAY 20
0
JavaScript

JavaScript為什麼筆記本系列 第 20

Day 20|我明明 count++了,為什麼畫面沒變?

  • 分享至 

  • xImage
  •  

Day 16 第一次碰到 useState 時,我先把它理解成:

const [user, setUser] = useState(null)

React 幫我記住一份資料,而要改它時,用 setter。

但這裡一直有一個很直覺的問題。

假設只是做計數器:

const [count, setCount] = useState(0)

按下按鈕:

setCount(count + 1)

畫面就會從 0 變成 1。

可是 JavaScript 本來就會加一啊:

count++

那為什麼還要繞一圈寫:

setCount(count + 1)

今天就只追這一題:

普通變數明明可以改,為什麼 React 還需要 state?


先故意不用 useState

先想像這個版本:

function Counter() {
  let count = 0

  function handleClick() {
    count++
    console.log(count)
  }

  return (
    <button onClick={handleClick}>
      點了 {count} 次
    </button>
  )
}

如果按三次按鈕,直覺上很容易猜:

點了 3 次

因為 count++ 明明有執行。

但這裡要先分開兩件事情:

JavaScript 裡的變數改變
≠
React 決定重新 render

在這個例子裡,點擊事件確實可以把目前那個 count 加一。

所以 Console 可能一路看到:

1
2
3

但單純修改普通區域變數,不會通知 React:

這份資料改了,請重新算一次畫面。

因此 JSX 裡原本 render 出來的:

點了 0 次

不會因為 count++ 自動更新。

[!NOTE]
這篇目前是草稿,repo 裡還沒有用實際 React 專案跑過這個例子。上面的觀察結果仍應在後續實作時用 Console 與畫面一起驗證。


count++ 到底做了什麼?

先完全離開 React。

let count = 0

count++

console.log(count)

結果就是:

1

JavaScript 的工作沒有問題。

這行:

count++

做的是:

把這個 JavaScript 變數的值改掉

但 React 不會監聽程式裡每一個普通變數,只要它一變就重畫整個 Component。

如果真的那樣做,React 根本無法知道哪些變化只是暫存計算,哪些變化是真的會影響 UI。

所以問題不是:

count++ 有沒有成功?

而是:

React 有沒有被告知這個變化需要反映到畫面?


setCount 多做了什麼?

換回 React 的版本:

import { useState } from 'react'

function Counter() {
  const [count, setCount] = useState(0)

  function handleClick() {
    setCount(count + 1)
  }

  return (
    <button onClick={handleClick}>
      點了 {count} 次
    </button>
  )
}

假設目前這次 render 裡:

count = 0

按下按鈕後:

setCount(count + 1)

不是直接把眼前這個 count 變數改成 1。

比較適合目前階段的理解是:

setCount(1)
↓
告訴 React:下一份 state 要更新成 1
↓
React 安排重新 render
↓
Component 再執行一次
↓
新的一次 render 取得新的 count
↓
JSX 再算一次
↓
畫面更新

所以 setter 的關鍵不只是「改值」。

它是透過 React 的 state 更新機制,讓 React 知道:

這份資料改了,而且這份資料屬於畫面狀態。


Component 重新執行,普通變數不是也會重新開始?

這又是另一個差異。

如果寫:

function Counter() {
  let count = 0

只要 Component 因為任何原因重新 render,function 就會再執行一次。

也就是又走到:

let count = 0

普通區域變數不會自動幫我保存「上一次 render 的 UI 狀態」。

可以先把它想成:

第一次 render
Counter()
↓
let count = 0

下一次 render:

Counter()
↓
又建立新的區域變數
↓
let count = 0

但 state 不一樣。

const [count, setCount] = useState(0)

第一次 render 時,初始值是:

count = 0

之後如果 React 接到:

setCount(1)

下一次 render 時,它會把目前保存的 state 值提供回來。

所以 useState(0) 裡的 0 不是每次 render 都強迫把 count 洗回 0。

目前可以先讀成:

這份 state 第一次建立時,初始值是 0;之後由 React 幫我保存目前狀態。


原來普通變數和 state 解決的是不同問題

以前看到:

const [count, setCount] = useState(0)

很容易覺得 React 只是把:

let count = 0

寫得更麻煩。

但如果把責任拆開,就比較清楚了。

普通變數:

拿來做 JavaScript 計算
↓
值可以在目前程式流程中改變

React state:

保存跨 render 需要記住的 UI 狀態
+
透過 setter 提出更新
+
讓 React 有機會重新 render

所以問題其實不是:

為什麼 React 不准我用 count++

而是:

這個數字是不是一份會影響 UI,而且需要跨 render 保留下來的資料?

如果是,state 就有它存在的理由。


今天真正想記住的

不是只有這行語法:

const [count, setCount] = useState(0)

而是:

普通變數改變
≠
React state 更新

以及:

setCount(...)
↓
提出 state 更新
↓
React 重新 render
↓
Component 再執行
↓
取得新的 state
↓
JSX 重新計算
↓
畫面更新

這樣再回頭看:

setCount(count + 1)

就比較不像一個硬背的 React 咒語。

它其實是在告訴 React:

這個值不是只有 JavaScript 自己要知道,下一次畫面也需要知道。



上一篇
Day 19|&& 明明是邏輯運算子,為什麼可以拿來顯示 React 元件?
系列文
JavaScript為什麼筆記本20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言