回到部落格

2023-04-19

【React 新手村】Hooks-useState()

【React 新手村】Hooks-useState()

原文發表於 Medium

React Hooks的辨認方式就是使用use開頭的名稱,像useState就是React工程師最常使用的Hooks之一,也是建構一個互動式React網頁的必要元素。

顧名思義,useState就是用來控制『狀態』的。但非資訊相關領域出身的我當時就有個疑惑:「什麼是狀態?」。

用個比方來回答這個問題:假設你買了兩件一樣的衣服,在剛拿到手的時候,這兩件衣服是沒有任何分別的,之後你拆封了其中一件,那麼那件衣服的『狀態』就會變成『已拆封』,所以兩件衣服就有了區別。接著伴隨你使用狀況的不同,例如將衣服下水洗了,那又演變出更多不同「狀態」了。

那這跟網頁有什麼關係?當然有,因為每個使用者連結到的網頁都是同一個,但隨著使用者與畫面的互動(例如加入某個商品進購物車),這些網頁就會有不同的狀態,所以網頁元件都是同一個,但內容不同。在React上,我們利用useState來記憶網頁的初始值,並且在需要變化狀態時,記憶新的狀態。

在新的React專案中,著手建立第一個useState Hook,我們在/src資料夾下方建立一個Counter.jsx檔案:

//Counter.jsx

import React,{useState} from "react"  
  
  
function Counter() {  
  
const [myNumber,setMyNumber] = useState(0)  
//useState的標準寫法  
  
function onClickButtonHandler(){  
  setMyNumber(  
  (prev)=> prev+1  
  )  
}  
//點擊按鈕會觸發的函式  
  
return(  
  <div>  
  {myNumber === 0 ?  
  <p>你尚未點擊任何一個按鈕</p> :  
  <p>這是你點擊第{myNumber}按鈕</p>  
  }  
  <button onClick={onClickButtonHandler} >{/*將函式掛上onClick*/}  
  點擊有驚喜!  
  </button>  
    
  </div>  
)  
  
}  
  
export default Counter

在這個檔案中,我設計了一段文字,並且會隨著使用者點擊網頁按鈕,改變頁面上顯示的資料。

這一步完成之後,接下來就必須在App.jsx上把Counter這個component掛上去,讓Counter出現在DOM中(找時間再來寫一篇談關於DOM的概念):

import React from 'react';
import Counter from "./Counter"

export function App(props) {  
  return (  
    <div className='App'>  
    <Counter/> {/*掛在這*/}  
    </div>  
  );  
}  
  
//記得import Counter元件,然後使用標籤的模式掛在return的內容中。

最後在command line輸入npm start 就會看到下方的畫面(或參考playcode https://playcode.io/1438115):

點擊按鈕之後可以發現畫面跟著變動了:

這一切是怎麼完成的?若我們看清楚Counter.jsx檔案的寫法就可以解開這個疑問。首先這個useState不是JavaScript原生的方法,因此必須要導入(import):

import React,{useState} from "react"

然後hooks有個使用的限制就是,它只能在React 元件,也就是Counter function中使用,使用的方式長這樣:

const [myNumber,setMyNumber] = useState(0)

//上面的內容可以如下方這樣理解:  
//const [目前值,設定值使用的function] = useState(初始值)

如果各位不熟悉 ES6的JavaScript語法,可能會對於這樣的寫法很陌生。這樣的寫法叫做「解構賦值」,解構什麼?就是useState()回傳的東西。

在我們執行useState時候,它會回傳一個Array(數列),也就是一個有順序的資料,通常我們使用方括號筐住這些資料,資料間使用逗點來分別彼此。

const someData = [
"我是第一筆資料(字串)",
function(){console.log("我是第二筆資料(函式)")}
]

然而我們在使用Array中的特定某個資料時,就使用它的索引值(index)來抓取資料,例如下方:

console.log(someData[0]) //主控台上顯示"我是第一筆資料(字串)"

someData[1]() //主控台上顯示"我是第二筆資料(函式)"

所以在使用useState時,你也可以這樣來用:

const state = useState(0)

state[0] //目前值  
state[1] //設定值使用的function

只是這樣不太方便也不容易閱讀,因此「解構賦值」就很好用,我們在定義const時,就以array的樣子作為名稱,

const [myNumber,setMyNumber] = useState(0)

意思就是說,等一下那個函式返回的array中,我要把第一筆資料取名叫myNumber,第二筆資料叫做setMyNumber,所以這邊你也可以換成自己喜歡的名字,例如:

const [num,setNum] = useState(0)

反正就只是取個名字罷了。

言歸正傳,在我們使用useState的資料後,它的具體使用方式為何?

首先,array 的第一筆資料,也就是目前值,在網頁第一次被打開的時候(技術用語是「第一次渲染時」),他的數值是由useState的引數決定的,所以在我們的例子中,初始的目前值就是0。

若我們要變動初始值,就必須要使用到回傳array的第二筆資料,我將它稱為Setter:

setMyNumber(
(prev)=> prev+1
)

這邊要說明這個特別的寫法,因為在官方的Docs中,setter的寫法是這樣的:

setMyNumber(
myNumber+1
)

你可能會想:「對嘛!既然要把目前值加1,為何不直接使用從useState回傳的目前值當基準呢?」,其實在只有一個觸發狀態變動的機制下,這樣的寫法完全沒問題,但若是有好多個input會連動造成目前值變動時,或者多層次渲染在執行時,這樣的寫法並不安全,有可能會沒有正確更新目前值,因此推薦養成習慣,使用前者。

在知道使用方法之後,我們就能了解到,component回傳的內容:

return(
<div>
{myNumber === 0 ?
<p>你尚未點擊任何一個按鈕</p> :
<p>這是你點擊第{myNumber}按鈕</p>
}
<button onClick={onClickButtonHandler} >
點擊有驚喜!
</button>
</div>
)

由於return的內容就已經是html的呈現,因此若要把變數加入到顯示的內容中,必須要使用大括號{ },因此在文字段落上才會這樣寫:

<p>這是你點擊第{myNumber}按鈕</p>

然後為了觸發setter,因此我把觸發setter的function綁定在按鈕的onClick上,當然你也可以使用Arrow Function這樣寫,就不用另外寫獨立的function:

<button onClick={()=>setMyNumber(
(prev)=> prev+1
)} >
點擊有驚喜!
</button>

最後,我要討論在React撰寫實務上時常用到的『條件運算子』(https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Conditional_operator)

在下方這段程式碼中,我告訴React,若是myNumber是0時,顯示「你尚未點擊任何一個按鈕」,然後其他情況下就顯示「這是你點擊第{myNumber}按鈕」:

{myNumber === 0 ?
<p>你尚未點擊任何一個按鈕</p> :
<p>這是你點擊第{myNumber}按鈕</p>
}

這個邏輯的表現方式是:

「條件 ? 若條件為true時的渲染內容 : 若條件為false 時的渲染內容」

重點是問號 “?” 跟冒號 “:”的位置。如此就可以利用『狀態』來控制畫面的渲染了。

感謝你的耐心閱讀,希望透過這篇,你已經對於useState的用法有個大概的了解。為了讓您更熟悉useState,請您給自己一個挑戰,建立一個React 元件,使用input輸入欄位來控制useState的值,若您順利完成了,您也就掌握了useState的使用方法了。