React Hook useState – React 白話文運動 11

React11-React-Hook-useState

前言

前一篇則針對 React 語法進一步做講解, React 生命週期 – React 白話文運動 10 學會了 React 生命週期三個階段,並且學會了類別物件、函式物件的生命週期。這一篇則用實例來解說 Hook 中的 useState:

  1. useState 是什麼?
  2. 建立一個評價的 UI 元件
  3. 針對此元件進行重構
  4. 為此元件加上 useState 的 hook
  5. 新增 setState 為評價元件新增修改功能
  6. 舊版的 React 狀態管理寫法

useState 是什麼?

useState 是 React 中的一個 Hook,用於在函式元件中添加狀態,透過 useState,我們可以在函式元件中定義和管理狀態資料。

useState 返回一個包含兩個值的陣列,其中包括:

  1. 當前的狀態:在元件首次渲染時,它的值將等同於我們傳遞給 useState 的初始值。
  2. set 函式:這個函式負責更新狀態的值,同時觸發元件的重新渲染。
				
					const [state, setState] = useState(initialState)
				
			

建立一個評價的UI元件

這邊使用 Alex Banks & Eve Porcello 所出的 React  教學來做講解,不過其中一些程式碼有稍作修改。

因為這一篇文章要建立一個 UI 頁面,因此需要使用  react-icons 套件,裡面有內建數百個 SVG 的圖庫。我們可以在 terminal 輸入以下的指令,即可下載 react-icons 套件。

				
					npm i react-icons
				
			

透過 建立React專案 – React 白話文運動 08 的教學步驟,快速建立一個 react 專案。

				
					npx create-react-app my-app
cd my-app
npm start
				
			

這時候就會在本地端啟動一個 react 專案,接下來修改一下 App.js 如以下,便可以獲得以下的 UI 元件。

				
					import React from 'react'
import { FaStar } from 'react-icons/fa'


export default function App() {
    return (<div>
        <FaStar color='red'></FaStar>
        <FaStar color='red'></FaStar>
        <FaStar color='red'></FaStar>
        <FaStar color='red'></FaStar>
        <FaStar color='red'></FaStar>
    </div>)
}

				
			
react-hook-usestate-01

針對此元件進行重構

重構(refactor)的意思是指:在不改變程式結果的同時,改寫程式碼,讓整體程式碼品質提升。

接下來我們要根據原有的程式碼進行重構,讓整體程式碼變得更簡潔。這裡可以分成三個部分做講解:

重構第一步

可以先把 FaStar 拆成獨立的元件,並且針對這個元件給予參數,這邊給予的參數為 selected 引數,預設值為 true。

				
					const Star = ({ selected = true }) => (
    <FaStar color={selected ? 'red' : 'gray'}></FaStar>
)

				
			

重構第二步

建立一個函數,只要給予長度,就可以快速組合出一個陣列。

				
					const createArray = length => [...Array(length)];
				
			

重構第三步

最後在使用的 UI 裡面,使用 ES6 陣列的 map,在 map 函式中會回傳對應的 index 以及元件。

				
					export default function App({ totalStar = 5 }) {
    return createArray(totalStar).map((n, i) => <Star key={i} />)
}

				
			

當完成重構後,原本的程式執行結果是不變的,最後依然會回傳五個星星。

				
					import React from 'react'
import { useState } from 'react';
import { FaStar } from 'react-icons/fa'

const Star = ({ selected = true }) => (
    <FaStar color={selected ? 'red' : 'gray'}></FaStar>
)

const createArray = length => [...Array(length)];

export default function App({ totalStar = 5 }) {
    const [selectedStars] = useState(3)
    return createArray(totalStar).map((n, i) =>
        <Star key={i} selected={selectedStars > i} />)
}

				
			

為此元件加上 useState 的 hook

重構後的程式碼,尚未加上任何相關的資料或是狀態。這邊我們可以針對函式元件,使用一些 hook 來進行狀態管理。

useState( ) 就是我們在學習 hook 中第一個會學習到的函式,我們可以透過以下語法,給予一個元件的狀態 。

				
					const [variable,setVariable] = useState("");
				
			

以下列程式碼的例子來說,我們給 App 這個函式元件新增了一種狀態,稱為 selctedStars。並且設定值 selctedStars 為 3,那在這個元件中,我們就可以針對這個變數去做對應的渲染。

				
					import React from 'react'
import { useState } from 'react';
import { FaStar } from 'react-icons/fa'

const Star = ({ selected = true }) => (
    <FaStar color={selected ? 'red' : 'gray'}></FaStar>
)

const createArray = length => [...Array(length)];

export default function App({ totalStar = 5 }) {
    const [selectedStars] = useState(3)
    return createArray(totalStar).map((n, i) => <Star key={i} selected={selectedStars > i} />)
}
				
			

因為設定了 selctedStars 為 3,並且也在 FaStar 元件中設定了判別式。如果 selected 是 true 則顯示紅色,否則顯示黑色,最後就會有以下元件。

react-hook-usestate-02

setState 新增修改功能

useState( ) 除了可以給予預設值以外,也可以修改其值,這邊可以使用 setState來去做修改。

可以在 StarRating 中,新增一個  setSelectedStars,就可以在元件中做使用。這邊也在 Star 裡面新增一個函數引數,稱為 onSelect ,並且將 setSelectedStars 傳進去。

				
					export function StarRating({ totalStars = 5 }) {
    const [selectedStars, setSelectedStars] = useState(3)
    return <div>
       {createArray(totalStars).map((n, i) => 
		<Star 
    		key={i} 
    		selected={selectedStars > i} 
    		onSelect={() => setSelectedStars(i + 1)} />
		)}
        <p>
            {selectedStars} of {totalStars} stars
        </p>
    </div>
}
				
			

這邊再將傳進來的函數引數 setSelectedStars,傳入 onClick 的函數中,則所有的星星元件點擊之後,都會觸發 setSelectedStars( )。

				
					const Star = ({ selected = true, onSelect = f => f }) => (
    <FaStar color={selected ? 'red' : 'gray'} onCLick={onSelect}></FaStar>
)
				
			

這邊也附上完整的 useState 程式碼:

				
					import React from 'react'
import { useState } from 'react';
import { FaStar } from 'react-icons/fa'

const Star = ({ selected = true, onSelect = f => f }) => (
    <FaStar color={selected ? 'red' : 'gray'} onCLick={onSelect}></FaStar>
)

const createArray = length => [...Array(length)];

export function StarRating({ totalStars = 5 }) {
    const [selectedStars, setSelectedStars] = useState(3)
    return <div>
        {createArray(totalStars).map((n, i) => 
		<Star 
    		key={i} 
    		selected={selectedStars > i} 
    		onSelect={() => setSelectedStars(i + 1)} />
		)}
        <p>
            {selectedStars} of {totalStars} stars
        </p>
    </div>
}

				
			

舊版的 React 狀態管理寫法

舊版是指比較早期的類別元件寫法,因為 hook 只能用於函式元件。這邊則是展示類別元件的狀態管理,不過因為類別元件本身有一些物件導向的概念,會比較難理解。

相較於 useState 去做資料狀態的預設以及更新,類別元件中使用的是建構子(constructor),建構子可以給予 starSelected 的預設值為 0,並且也另外針對類別中宣告的函式 change 進行綁定,最後再透過 render( ) 函式去做元件的渲染。

				
					import React, { Component } from 'react'
import { FaStar } from 'react-icons/fa'


const Star = ({ selected = true }) => (
    <FaStar color={selected ? 'red' : 'gray'}></FaStar>
)

const createArray = length => [...Array(length)];


export default class StarRating extends Component {
    constructor(props) {
        super(props);
        this.state = {
            starSelected: 0
        };
        this.change = this.change.bind(this);
    }

    change(starSelected){
        this.setState({starSelected});
    }

    render(){
        const {totalStars} = this.props;
        const {starSelected} = this.state;
        return (
            <div>
                {[...Array(totalStars)].map((n,i)=>{
                    <Star 
                        key={i} 
                        selected={i < starSelected} 
                        onClick={()=>this.change(i+1)}
                    />
                })}
                <p>
                    {starSelected} of {totalStars} stars
                </p>
            </div>
        )
    }
}
				
			
zh_TW繁體中文