快速入門

歡迎來到 React 文件!這個頁面將介紹你每天會用到的 80% 的 React 概念。

你將學習

  • 如何建立和巢狀元件
  • 如何新增標記和樣式
  • 如何顯示資料
  • 如何渲染條件和列表
  • 如何回應事件並更新畫面
  • 如何在元件之間共享資料

建立和巢狀元件

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

React 元件是傳回標記的 JavaScript 函式

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

現在你已經宣告了 MyButton,你可以將它巢狀到另一個元件中

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

請注意,<MyButton /> 以大寫字母開頭。這就是你如何知道它是一個 React 元件。React 元件名稱必須始終以大寫字母開頭,而 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 指定檔案中的主要元件。如果你不熟悉某些 JavaScript 語法,MDNjavascript.info 提供了很好的參考。

使用 JSX 編寫標記

你在上面看到的標記語法稱為*JSX*。它是可選的,但大多數 React 專案都使用 JSX 以求方便。所有我們推薦用於本地開發的工具都原生支援 JSX。

JSX 比 HTML 更嚴格。你必須關閉像 <br /> 這樣的標籤。你的元件也不能傳回多個 JSX 標籤。你必須將它們包裝在一個共享的父元素中,例如 <div>...</div> 或一個空的 <>...</> 包裝器

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

如果你有很多 HTML 要移植到 JSX,可以使用線上轉換器

新增樣式

在 React 中,你使用 className 指定 CSS 類別。它的作用方式與 HTML class 屬性相同

<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 屬性中「跳入 JavaScript」,但您必須使用大括號而不是引號。例如,className="avatar" 會將 "avatar" 字串作為 CSS 類別傳遞,但 src={user.imageUrl} 會讀取 JavaScript user.imageUrl 變數值,然後將該值作為 src 屬性傳遞

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 大括號內的普通 {} 物件。當您的樣式取決於 JavaScript 變數時,您可以使用 style 屬性。

條件渲染

在 React 中,沒有用於編寫條件的特殊語法。相反,您將使用與編寫常規 JavaScript 程式碼時相同的技巧。例如,您可以使用 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>

所有這些方法也適用於有條件地指定屬性。如果您不熟悉其中一些 JavaScript 語法,您可以始終使用 if...else

渲染清單

您將依賴 JavaScript 功能,例如 for 迴圈陣列 map() 函式 來渲染組件清單。

例如,假設您有一個產品陣列

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

在您的組件內,使用 map() 函式將產品陣列轉換為 <li> 項目的陣列

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

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

請注意 <li> 如何具有 key 屬性。對於清單中的每個項目,您應該傳遞一個字串或數字,以在其兄弟姐妹中唯一地標識該項目。通常,金鑰應該來自您的資料,例如資料庫 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>
  );
}

回應事件

您可以透過在組件內宣告*事件處理常式*函式來回應事件

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

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

請注意 onClick={handleClick}結尾沒有括號!不要*呼叫*事件處理常式函式:您只需要*將其傳遞*。當使用者點擊按鈕時,React 將會呼叫您的事件處理常式。

更新畫面

通常,您會希望您的組件「記住」一些資訊並顯示它。例如,也許您想計算按鈕被點擊的次數。要做到這一點,請將*狀態*添加到您的組件中。

首先,從 React 導入 useState

import { useState } from 'react';

現在您可以在組件內宣告一個*狀態變數*

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

您將從 useState 獲得兩樣東西:目前狀態 (count) 和允許您更新它的函式 (setCount)。您可以給它們任何名稱,但慣例是寫成 [某事物, 設定某事物]

第一次顯示按鈕時,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 會再次呼叫你的元件函式。這次,count 將會是 1。然後它會是 2,依此類推。

如果你多次渲染相同的元件,每個元件都會有自己的狀態。請分別點擊每個按鈕。

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 狀態,並且不會影響其他按鈕。

使用 Hooks

use 開頭的函式稱為 *Hooks*。useState 是 React 提供的內建 Hook。你可以在 API 參考 中找到其他內建 Hooks。你也可以透過組合現有的 Hooks 來編寫自己的 Hooks。

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

在元件之間共享數據

在前面的範例中,每個 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 將其 count 更新為 1

但是,你通常需要元件*共享數據並始終一起更新*。

要讓兩個 MyButton 元件顯示相同的 count 並一起更新,你需要將狀態從個別按鈕「向上」移動到包含所有按鈕的最接近的元件。

在此範例中,它是 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 狀態為 0,並向下傳遞給兩個子元件。

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.

點擊時,MyApp 將其 count 狀態更新為 1,並向下傳遞給兩個子元件。

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

首先,將狀態從 MyButton *向上移動*到 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 *向下傳遞*到每個 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 元件包含 count 狀態和 handleClick 事件處理常式,並將*兩者都作為屬性傳遞*給每個按鈕。

最後,更改 MyButton 以*讀取*你從其父元件傳遞的屬性。

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

當你點擊按鈕時,onClick 處理常式會觸發。每個按鈕的 onClick 屬性都設定為 MyApp 內部的 handleClick 函式,因此其中的程式碼會執行。該程式碼會呼叫 setCount(count + 1),遞增 count 狀態變數。新的 count 值會作為屬性傳遞給每個按鈕,因此它們都會顯示新的值。這稱為「狀態提升」。透過向上移動狀態,你已在元件之間共享它。

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 建立你的第一個迷你應用程式。