前端框架

-

React 教學系列文 1 - 學 React 一定要先理解的基礎重要概念

this.web

上一篇 - 你為甚麼要學 React 中,很完整的介紹你為甚麼要學 React,如果你只是覺得大家說要學你才學,那我推薦你去看上一篇文章,今天就正式進入 React 教學~

React 基礎概念

在正式進入 React 之前,要先來說說 React 幾個很重要觀念,這些觀念適用於整個前端生態,包括其它框架,而很多新手在接觸 React 或其它框架時,會覺得很不習慣或難以上手,有很大一部份就是因為沒有先建立起這方面的觀念,所以我會花一小個篇幅先來講講這些 React 觀念,整個架構會是:

  1. JSX
  2. 組件 Components
  3. 屬性 Props
  4. 狀態 State

JSX

JSX 是 React 很重要也很基本的語法,其實他就像是在 JS 裡寫 HTML,比如我們可以宣告一個變數,變數的值就是我們平常寫的 HTML。

const title = <h1>Hello, world!</h1>

特別的是,在 JSX 中,我們可以用花括號 {} 來結合 JavaScript,比如說。

const message = 'Hello, World!'
const title = <h1>{ message }</h1>
// <h1>Hello, world!</h1>

這就是 JSX 的好處。

更進階可以他配 template literals 樣板字面值 - JavaScript | MDN (mozilla.org) 來使用,例如

const message = 'Hello, World!'
const title = <h1>{ `This is my message: "${message}"`}</h1>
//<h1>This is my message: "Hello, world!"</h1>

當然我們也可以添加 HTML 的屬性在 JSX 裡,但要注意的是,因為我們是在 JS 哩,所以有些與法會和在 HTML 裡不太一樣。

1. className : 在 JSX 裡要添加 class,必須要寫 className

const title = <h1 class="heading">{ message }</h1> // 錯誤
const title = <h1 className="heading">{ message }</h1> // 正確

這是因為在 JS 中,已經有 class 這個關鍵字了,所以要用 className 代替。

2. 駝峰式命名 (camel) : 在 JSX 哩,屬性的名稱要用駝峰式命名代替

const title = <h1 onclick={() => alert('hello')}>{ message }</h1> // 錯誤
const title = <h1 onClick={() => alert('hello')}>{ message }</h1> // 正確

例如點擊事件是 onclick,但在 JSX 裡就要用 onClick

但 aria-* 和 data-* 就不需要,因為中間已經有 - 連接了。

補充:其實 JSX 在最後也會被轉譯成 JS 格式,例如

const message = 'Hello, World!';
const element = <h1>{message}</h1>;

// 變成

const message = 'Hello, World!';
const element = React.createElement('h1', null, message);

這個轉換過程是由 Babel 等工具完成的,我們現在不太需要關心具體的實現。

組件 Component

可以想像一個頁面試好幾個組件組成的,例如 Header 組件、Button 組件、Footer 組件 ... 等等。每個組件都擁有自己的結構、樣式、行為邏輯。

以往我們用原生 JS,如果網頁上有很多個類似的按鈕,我們可能會重複寫非常多類似的邏輯或樣式,比如:

<button class="btn btn-primary">My Button</button>
<button class="btn btn-outline">My Button</button>
<button class="btn btn-underscore">My Button</button>

每個 button 又要寫額外的 style 或事件邏輯,這會讓網站難以維護。

所以組件的好處就是可以讓我們重複使用相同的結構,而在 React 裡,一切皆是函數,一個函數就代表一個組件。舉個簡單例子,我們可以創建一個 Button 函數組件,並且將 JSX 用 括號 () 括起來:

unction Button() {
  return (
    <button class="btn">
      My Button
    </button>
  );
}

Button 函數返回的值就是上面提到的 JSX 。要注意的有三件事情

  1. 如果 return 的 JSX 超過一行,就要用 () 括起來
  2. 組件的名稱開頭一定要是大寫,這是為了讓 React 區別是一般 HTML tag 還是我們自訂一的組件
  3. 我們一次只能返回一個父元素,比如
// 錯誤
function Button() {
  return (
    <label>This is a button</label>
    <button class="btn">
      My Button
    </button>
  );
}

我們要用一個父元素將其包起來:

// 正確
function Button() {
  return (
    <div>
      <label>This is a button</label>
      <button class="btn">
        My Button
      </button>
    </div>
  );
}

在 React 中,我們也可以用一個空的 tag <> 來包住子元素

// 正確
function Button() {
  return (
    <>
      <label>This is a button</label>
      <button class="btn">
        My Button
      </button>
    </>
  );
}

最後我們就可以這樣來使用組件。

<Button />
<Button />
<Button />

你可能會覺得這樣還是需要根據每個按鈕來寫 style 或事件。props 屬性就可以解決這個問題。

屬性 Props

我們可以傳入參數給 React 函數組件,也就是所謂的 Props 屬性,這樣的好處是讓組件的複用性更高,例如我們希望可以自由地調整 Button Background 和文字,並且想針對不同的 Button 有不同的點擊效果,我們就可以稍微改寫一下上面的例子:

function Button(props) {
  return (
    <button
      onClick=(props.onClick)
      style={{backgroundColor: props.color}}>
      {props.text}
    </button>
  );
}

這個 Button 組件接收三個屬性參數: color 、 text 和 onClick。當我們使用這個組件時,只需要傳入不同的屬性值,就能夠快速打造多個有相同架構的按鈕:

<Button color="blue" text="Click Me" onClick={console.log('you click me')} />
<Button color="red" text="Don't Click" onClick={console.log('don't click)}/>

這樣就能渲染出兩個不同顏色和文字的按鈕,並且有不同的 console。

簡單說,我們可以把 props 想像成組件的 "inputs" 或 "參數",它們決定了組件的外觀和行為。比如前面 Button 組件的例子,color 、 text 和 onClick 就是組件的三個參數。

所以組件化的優勢在於可重用性。一旦創建好一個通用組件,無論在應用的任何地方,我們都可以輕鬆地複用它。這種模塊化設計大大提高了開發效率和程式的可維護性。

當然,我們也可以使用ES6的解構語法簡化書寫:

function Button({ color, text, onClick }) {
  return (
    <button
      onClick={onClick}
      style={{backgroundColor: color}}>
      {text}
    </button>
  );
}

現在有個問題,如果我們希望點及按鈕後,更改按鈕的文字,我們該怎麼做呢?這就要提到狀態 state。

狀態 State

在程式世界裡,如果隨意更改函數 input,會讓程式碼變得非常不好維護,因為我們不知道 input 究竟變成甚麼樣子。舉例來說

function sum(num1, num2) {
  num1 = num1 * 2; // 隨意更改 input 的值
  return num1 + num2;
}

let x = 1, y = 2;
let z = sum(x ,y); 
// 預期是 1 + 2 = 3
// 但因為更改了 input,變成 1 * 2 + 2 = 4

比較好的做法是在函數裡新增一另一個變數來操作:

function sum(num1, num2) {
  let num3 = num1 * 2;
  // 執行要用到 num3 的操作
  return num1 + num2;
}

相對的,在 React 中,我們也不能隨意更改組件的 prop 值,也因此,如果我們要更新組件的文字,我們一樣需要在組件新增一個變數,也就是狀態 state 來控制組件。

每個組件都可以有自己的 state,當 state 更新的時候,畫面會跟著更新,我們在稍微改寫一下 Button 組件。

function Button(props) {
  const [myText, setMyText] = useState(props.text) // 這是在 React 中宣告 state 的語法
  const changeText = () => {
    setMyText('你點擊我了!') // 並且我們只能用 set... 來更改 state
  }
  
  return (
    <button
      onClick={changeText}
      style={{backgroundColor: props.color}}>
      {myText}
    </button>
  );
}

useState 是 React 宣告狀態的方法,傳入的值會是 state 的初始值,且只能用 setState 來改變 state 的值,這樣 React 才會知道狀態改變,UI 也要跟著改變。

上面程式碼解釋是:

  1. useState 創建一個狀態變量 myText 和一個更新這個狀態的函式setMyText。
  2. 定義了一個名為changeText的函式,當調用這個函式時,它會使用setMyText函式來更新myText狀態的值為"你點擊我了!"
  3. 最後,組件返回了一個 JSX 表示的button元素。這個按鈕被設置了一個點擊事件處理器,當按鈕被點擊時會調用changeText函式,從而更新按鈕上顯示的文字。

簡單說,我們可以把 state 看作是組件的"內部數據源",它決定了組件在特定時間點的呈現樣子。一旦狀態發生變化。就會自動觸發重新渲染組件的機制。

以上就是對 React 基礎概念的詳細解釋,希望通過案例和比喻,能讓你對 JSX、組件、props、state 核心理念有初步的理解。

小結

今天我們講了 React 中很重要的觀念,分別是

  1. JSX : 類似再 JS 裡寫 HTML,可以用 {} 來結合 JS
  2. 組件化:React 中每個組件都是一個函數,且返回 JSX
  3. 屬性:傳入參數當作組件的屬性,可以更好操控利用組件
  4. 狀態:每個組件都有自己的狀態,只有狀態改變,UI 才會改變,且要使用 React 的 useState 才能宣告狀態。

相關系列文章