快速開始

歡迎來到 React 文件!本頁將為你介紹日常使用的 80% React 概念。

You will learn

  • 如何建立和巢狀 component
  • 如何加入標記語言和樣式
  • 如何顯示資料
  • 如何 render 條件和列表
  • 如何回應事件和更新畫面
  • 如何在 component 之間共享資料

建立和巢狀 component

React 應用程式是由 components 組成的。Component 是具有自己的邏輯和外觀的 UI(使用者介面)的一部分。Component 可以像按鈕一樣小,也可以像整個頁面一樣大。

React component 是回傳標記語言的 JavaScript 函式。

function MyButton() {
return (
<button>I'm a button</button>
);
}

現在你已經宣告 MyButton,你可以將它嵌入另一個 component 中。

export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}

注意 <MyButton /> 開頭是大寫字母,這就是你知道它是 React component 的方式。React component 名稱一定要以大寫字母開頭,而 HTML 標記語言則必須為小寫字母。

請看一下結果:

function MyButton() {
  return (
    <button>
      I'm a button
    </button>
  );
}

export default function MyApp() {
  return (
    <div>
      <h1>Welcome to my app</h1>
      <MyButton />
    </div>
  );
}

export default 關鍵字指定檔案中的主要 component。如果你對某些 JavaScript 語法不熟悉,MDNjavascript.info 都有很棒的參考資料。

使用 JSX 撰寫標記語言

你所看到的標記語法稱為 JSX。它是可選的,但大多數 React 專案都會因其方便性而使用 JSX。我們推薦的本機開發工具都預設支援 JSX。

JSX 比 HTML 更嚴格。你必須正確地封閉標記,例如 <br />。你的 component 也不能回傳多個 JSX 標記,你必須將它們包裹在共同的 parent 標記中,例如 <div>...</div> 或空的 <>...</> wrapper 中:

function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there.<br />How do you do?</p>
</>
);
}

如果你有大量的 HTML 要移植到 JSX,你可以使用線上轉換

加入樣式

在 React 中,你可以使用 className 來指定 CSS 類別,它的功能與 HTML 的 class attribute 相同:

<img className="avatar" />

接著你可以在單獨的 CSS 檔案中撰寫對應的 CSS 規則:

/* In your CSS */
.avatar {
border-radius: 50%;
}

React 不規定如何加入 CSS 文件。在最簡單的情況下,你可以在 HTML 中增加一個 <link> 標籤。如果你使用的是構建工具或框架,請查閱其文件以了解如何將 CSS 文件加入到你的專案中。

顯示資料

JSX 讓你可以將標記語言放入 JavaScript。大括號讓你「逃回」 JavaScript,以便你可以嵌入一些來自你的程式碼的變數,並將其顯示給使用者。例如,這將顯示 user.name

return (
<h1>
{user.name}
</h1>
);

你也可以從 JSX attribute 「逃脫到 JavaScript」 ,但你必須使用大括號而不是引號。例如,className="avatar""avatar" string 作為 CSS class 傳遞,但 src={user.imageUrl} 讀取 JavaScript user.imageUrl 變數值,將該值作為 src attrubute 傳遞:

return (
<img
className="avatar"
src={user.imageUrl}
/>
);

你可以將更複雜的表達式放入 JSX 大括號中,例如字串串接

const user = {
  name: 'Hedy Lamarr',
  imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
  imageSize: 90,
};

export default function Profile() {
  return (
    <>
      <h1>{user.name}</h1>
      <img
        className="avatar"
        src={user.imageUrl}
        alt={'Photo of ' + user.name}
        style={{
          width: user.imageSize,
          height: user.imageSize
        }}
      />
    </>
  );
}

在上述範例中,style={{}} 並非特殊語法,而是在 style={ } JSX 大括號中的一個普通的 {} object。當你的樣式取決於 JavaScript 變數時,可以使用 style 屬性。

條件式 rendering

在 React 中,沒有特殊的語法可以撰寫條件。相反,你會使用相同撰寫一般 JavaScript 的技巧來撰寫 React 程式碼。例如,你可以使用 if 條件式語句來包含 JSX:

let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
);

如果你偏好更緊湊的程式碼,你可以使用 ? 條件運算子。if 不同,它可以在 JSX 內部運作:

<div>
{isLoggedIn ? (
<AdminPanel />
) : (
<LoginForm />
)}
</div>

當你不需要 else 分支時,你也可以使用更簡短的 && 邏輯語法

<div>
{isLoggedIn && <AdminPanel />}
</div>

這些方法也適用於有條件地指定 attribute。如果你對這些 JavaScript 語法不熟悉,你可以一開始就使用 if...else

Rendering 列表

你將會依賴 JavaScript for looparray map() 函式功能來 render component 的列表。

例如,如果你有一個產品的列表:

const products = [
{ title: 'Cabbage', id: 1 },
{ title: 'Garlic', id: 2 },
{ title: 'Apple', id: 3 },
];

在你的 component 內,使用 map() 函式將一個產品的 array 轉換成一個 <li> 項目的 array:

const listItems = products.map(product =>
<li key={product.id}>
{product.title}
</li>
);

return (
<ul>{listItems}</ul>
);

請注意 <li> 有一個 key attribute。對於列表中的每個項目,你應該傳遞一個 string 或 number,以便在其兄弟元素中識別該唯一項目。通常,鍵值應該來自於你的資料,例如資料庫的 ID。React 使用這些鍵值來了解後續的插入、刪除或重新排序項目時所發生的變化。

const products = [
  { title: 'Cabbage', isFruit: false, id: 1 },
  { title: 'Garlic', isFruit: false, id: 2 },
  { title: 'Apple', isFruit: true, id: 3 },
];

export default function ShoppingList() {
  const listItems = products.map(product =>
    <li
      key={product.id}
      style={{
        color: product.isFruit ? 'magenta' : 'darkgreen'
      }}
    >
      {product.title}
    </li>
  );

  return (
    <ul>{listItems}</ul>
  );
}

事件回應

你可以透過在 component 中宣告 event handler 函式來回應 event:

function MyButton() {
function handleClick() {
alert('You clicked me!');
}

return (
<button onClick={handleClick}>
Click me
</button>
);
}

注意 onClick={handleClick} 最後沒有括號!不要呼叫 event handler:你只需要將它傳遞下去。當使用者點擊按鈕時,React 將呼叫你的 event handler。

更新畫面

有時候,你想要 component「記住」一些資訊並呈現它。例如,你可以想要計算按鈕按過的次數。要達成這個目標,加入 state 到你的 component。

首先,從 React import useState

import { useState } from 'react';

現在你在 component 內宣告了一個 state 變數

function MyButton() {
const [count, setCount] = useState(0);
// ...

你會從 useState 得到兩個東西:目前的 state(count)以及一個函式讓你可以更新 state(setCount)。你可以給它們任何的命名,但慣例是寫成 [something, setSomething]

第一次顯示按鈕時,count 將為 0,因為你將 0 傳遞給 useState()。 當你想改變狀態時,呼叫 setCount() 並將新值傳遞給它。點擊此按鈕將增加計數器:

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

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

return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}

React 將會再次呼叫你的 component 函式。這個時候,count 會變成 1,然後它將變成 2,以此類推。

如果你 render 相同的 component 多次,每個 component 都會有自己的 state。分別點擊各個按鈕試試:

import { useState } from 'react';

export default function MyApp() {
  return (
    <div>
      <h1>Counters that update separately</h1>
      <MyButton />
      <MyButton />
    </div>
  );
}

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

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

  return (
    <button onClick={handleClick}>
      Clicked {count} times
    </button>
  );
}

注意每個按鈕如何「記住」它本身的 count state 並且不影響其他的按鈕。

使用 Hooks

use 開頭的函式稱為 HooksuseState 是 React 提供的內建 Hook。你可以在 API 參考中找到其他內建的 Hooks,你也可以結合現有的 Hooks 撰寫自己的 Hooks。

Hooks 比其他功能更具限制性。你只能在 component 的頂部 呼叫 Hooks(或其他 Hooks)。如果你想在條件或循環中使用 useState,請提取一個新 component 並將 Hooks 放在那裡。

在 component 之間共享資料

在前面的範例當中,每個 MyButton 都有自己獨立的 count,當每個按鈕被點擊時,只有被點擊按鈕的 count 發生了變化:

Diagram showing a tree of three components, one parent labeled MyApp and two children labeled MyButton. Both MyButton components contain a count with value zero.
Diagram showing a tree of three components, one parent labeled MyApp and two children labeled MyButton. Both MyButton components contain a count with value zero.

最初,每個 MyButtoncount 狀態都是 0

The same diagram as the previous, with the count of the first child MyButton component highlighted indicating a click with the count value incremented to one. The second MyButton component still contains value zero.
The same diagram as the previous, with the count of the first child MyButton component highlighted indicating a click with the count value incremented to one. The second MyButton component still contains value zero.

第一個 MyButton 更新 count1

但是,你通常需要 component 來共享資料並始終一起更新

要使兩個 MyButton component 顯示相同的 count 並一起更新,你需要將 state 從各個按鈕「向上」移動到包含所有按鈕的最近 component。

在這個範例中,它是 MyApp

Diagram showing a tree of three components, one parent labeled MyApp and two children labeled MyButton. MyApp contains a count value of zero which is passed down to both of the MyButton components, which also show value zero.
Diagram showing a tree of three components, one parent labeled MyApp and two children labeled MyButton. MyApp contains a count value of zero which is passed down to both of the MyButton components, which also show value zero.

一開始,MyAppcount state 是 0 並且傳遞給兩個 children

The same diagram as the previous, with the count of the parent MyApp component highlighted indicating a click with the value incremented to one. The flow to both of the children MyButton components is also highlighted, and the count value in each child is set to one indicating the value was passed down.
The same diagram as the previous, with the count of the parent MyApp component highlighted indicating a click with the value incremented to one. The flow to both of the children MyButton components is also highlighted, and the count value in each child is set to one indicating the value was passed down.

當 click 時,MyApp 更新它的 count state 成 1 並且傳遞給兩個 children

現在,當你點擊任一按鈕時,MyApp 中的 count 將發生變化,這將更改 MyButton 中的兩個計數。以下是你如何在程式碼中表達。

首先,將 MyButtonstate 移至上方MyApp

export default function MyApp() {
const [count, setCount] = useState(0);

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

return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
</div>
);
}

function MyButton() {
// ... we're moving code from here ...
}

接著,從 MyApp 往下傳遞 state 以及共享的 click handler 到每個 MyButton。你可以使用 JSX 大括號傳遞資訊到 MyButton,就像先前使用內建 <img> 標記語言一樣:

export default function MyApp() {
const [count, setCount] = useState(0);

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

return (
<div>
<h1>Counters that update together</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}

你像這樣傳遞下來的資訊叫做 props。現在 MyApp component 包含 count state 和 handleClick event handler,並將它們作為 props 傳遞 給每個按鈕。

最後,將 MyButton 更改為讀取 parent component 傳遞的來 props:

function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
Clicked {count} times
</button>
);
}

當你點擊按鈕時,將觸發 onClick handler。每個按鈕的 onClick prop 都設定為 MyApp 中的 handleClick 函式,所以它裡面的程式碼可以執行。該程式碼呼叫 setCount(count + 1),增加 count state 變數。新的 count 值作為 prop 傳遞給每個按鈕,因此它們都顯示新值。這被稱為「提升 state(lifting state up)」。通過向上移動 state,你已經在 component 之間共享它。

import { useState } from 'react';

export default function MyApp() {
  const [count, setCount] = useState(0);

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

  return (
    <div>
      <h1>Counters that update together</h1>
      <MyButton count={count} onClick={handleClick} />
      <MyButton count={count} onClick={handleClick} />
    </div>
  );
}

function MyButton({ count, onClick }) {
  return (
    <button onClick={onClick}>
      Clicked {count} times
    </button>
  );
}

下一步

現在,你已經知道如何撰寫基本的 React 程式碼了!

請查閱教學,將它們應用到實踐中,並用 React 建構你的第一個小應用程式。