React 入門 4 - Hooks: useState


Posted by urlun0404 on 2022-10-31

第0篇有提到state在React的重要性:

state 是用來驅動component更新(re-evaluate),並且根據修改的地方重新渲染(re-render)畫面。

在正式說明state之前,有個關於function component的重要性質必須提及:

重新渲染(re-render)畫面,代表重新呼叫(call)function component,所以component裡面的變數等都會重新建立

從JavaScript語法的角度試想一下,每次呼叫函式(function),函式內變數若沒有特別設計(如用閉包(Closure)保存一個區域變數的值),函式內變數的生命週期都會在函式結束呼叫後消失,並在函式呼叫時重新建立。

如下範例(live demo)即便使用 counter++; ,但因為變數 counter 的生命週期會在函式結束時消失,不管呼叫多少次 sayHello(); 仍然會得到 counter 為 1,而不是加總多次的 counter 變數值。

function sayHello(){
    let counter = 0;
    counter++;
    return counter;
}
sayHello();
sayHello();
sayHello();
console.log(sayHello());   // 1


同理,一般變數在React的function component在沒有特地保存的情況下,也會在畫面重新渲染(函式重新呼叫)時消失

為了保存函式內某些變數的值(或者有時會稱「要保存某些變數的狀態」),state 另一個重要用途就是保存(preserve) function component裡的值

若要在function component使用state,必須用React hooks其中的 useState,使用方式如下:

  • 引入模組 import {useState} from 'react';
  • 宣告React state變數 const [state, setState] = useState(0);
    • useState() 指的是要使用 useState 這個 Hook,useState() 括號裡面是state變數的初始值,可以是 0[]""{} 等,而useState會回傳一個陣列,包含目前的狀態 state 和可以用來改變狀態的 setState函式,可以用陣列解構(destructure)的方式取得:
    • state 是state變數名稱,也就是目前變數值或狀態
    • setState 是可以改變 state變數值的函式,慣例會以 set 加上

完整的使用方式可以參考live demo 或以下簡化live demo範例後的程式碼。

// 以下是Demo範例簡化後的程式碼
import { useState } from 'react';

export default function Counter(){
    const [count, setCount] = useState(0);   // React's state variable
    let counter = 0;

    return(
        <div>
            <p>counter: {counter}</p>
            <p>count: {count}</p>
            <button onClick={()=>counter++}>Add counter First</button>
            <button onClick={()=>setCount(count+1)}>Add count Second</button>
        </div>
    );
}

程式碼參考React文件設計兩個計數器,這裡刻意放了一個純JavaScript的函式內區域變數 counter 來比較 React 的 useState 變數 count。改變計數器狀態的方式是在button標籤裡面新增一個onClick事件,並且用匿名函式包裝一下 counter++ ,以及用callback函式去呼叫 setCount(count+1) (不能直接使用onClick = setCount(count+1) 會變成是直接呼叫setCount函式)

live demo 應該可以發現 counter 無法直接對他做改變,這裡我是理解成 counter 的生命週期會在函式結束時消失,所以沒辦法改變它。

而 React 則在 setCount(count+1) 之後,會改變 count 這個 state 的值,並且 re-render Counter component,然後將新的 count 值呈現在畫面上。


統整官方文件和範例結果的一些重點:

  1. Function component 裡面沒有 this,如果要保存 React function component 內的變數值或狀態需要用 useState(事實上也有其他的hooks可以保存變數狀態,但目前只介紹到useState);假設是用一般函式內的區域變數
    (例如範例的 counter),不僅變數值會消失,也無法改變變數值。
  2. 使用 useState 的方式是先引入模組,然後宣告 state 變數 (state)、改變 state值的函式(setState),在useState() 括號內初始化 state 變數的初始值,如:
    const [state, setState] = useState(/* initial value */);
  3. 改變state變數值時會連帶 re-render component,而React的useState不管是經過多少次的re-renders都可幫我們保存這些state變數值
  4. useState可以用陣列(Array)或物件(Objact)變數,所以如果要保存有高度關聯性的變數值,可以善用陣列或物件,而不是宣告一堆state變數。
  5. 最後React官方文件有提到state updates may be asynchronous,大意是,基於效能上的考量,React設計成會先知道有哪些states用 setState() 更新,並在下次渲染一次更新所有states;因此,使用 setState() 並非是立即更新state,如果需要取用前一次的state,則要在 setState() 內寫成function形式,並用funciton接收前一次狀態的參數來處理,如範例的 prevState
    setState( prevState =>  prevState + updatedValue );
    
  6. 由此可知,setState 的作用是:(1) 更新state (2) 告訴React要在下次畫面渲染更新指定的state

另外,如果component裡面有用 useState hook 管理狀態,稱這個component為 controlled component 或是 statefull/smart component;相反地,component裡面沒有管理state,則稱這種類型的components為 uncontrolled components 或是stateless/dumb/presentational components

最後題外話一下,從useState來看Hooks,Hooks 有點像是幫忙在函式內 住想保留的東西?🤔


References


#frontend #React







Related Posts

C#型別篇-1

C#型別篇-1

Join() - 將陣列內的文字連結,但是不要有逗號的方法!

Join() - 將陣列內的文字連結,但是不要有逗號的方法!

[Web] 轉換 [icon svg] 為 [background-image]

[Web] 轉換 [icon svg] 為 [background-image]


Comments