react 01

創建和嵌套組件

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>
  );
}

1、React 組件 必須以 大寫字母開頭,而 HTML 標簽則必須是小寫字母。
2、export default 關鍵字指定了文件中的 主要組件

使用 JSX 編寫標簽

上面所使用的標簽語法被稱為 JSX。它是可選的,但大多數 React 項目會使用 JSX,主要是它很方便。

JSX 比 HTML 更加嚴格。必須閉合標簽,如 <br />。你的組件也 不能返回多個 JSX 標簽。你必須將JSX標簽 包裹到一個共享的父級中,比如 <div>...</div> 或使用空的 <>...</> 包裹:

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

添加樣式

在 React 中,你可以使用 className 來指定一個 CSS 的 class。它與 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" 字符串傳遞給 className,作為 CSS 的 class。但src={user.imageUrl}會讀取 JavaScriptuser.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 循環和 array 的 map() 函數 來渲染組件列表。

假設你有一個產品數組:

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

在你的組件中,使用 map() 函數將這個數組轉換為 <li> 標簽構成的列表:

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>
  );
}

注意, <li> 有一個 key 屬性。對于列表中的每一個元素,你都應該傳遞一個字符串或者數字給 key,用于在其兄弟節點中唯一標識該元素。通常 key 來自你的數據,比如數據庫中的 ID。如果你在后續插入、刪除或重新排序這些項目,React 將依靠你提供的 key 來思考發生了什么。

響應事件

你可以通過在組件中聲明 事件處理 函數來響應事件:

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

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

注意,onClick={handleClick} 的結尾沒有小括號!不要調用 事件處理函數:你只需 把函數傳遞給事件 即可。當用戶點擊按鈕時 React 會調用你傳遞的事件處理函數。
當你傳遞onClick={handleClick} 時,你將 handleClick 函數作為 props 向下傳遞。如果加了(),相當于調用了個函數,而函數中setState()改變state,會讓頁面重新渲染,結果渲染時,又再次調用了這個函數,相當于進入了無限循環。

那么如何解決函數傳參的問題?

你可以通過創建調用 handleClick(0) 的函數(如 handleFirstSquareClick)、調用 handleClick(1) 的函數(如 handleSecondSquareClick)等來修復。你可以將這些函數作為 onSquareClick={handleFirstSquareClick}之類的 props 傳遞(而不是調用)。這將解決無限循環的問題。

但是,定義九個不同的函數并為每個函數命名過于冗余

讓我們這樣做:使用 新的 () => 語法,這里,() => handleClick(0) 是一個箭頭函數,它是定義函數的一種較短的方式。單擊方塊時,=>“箭頭”之后的代碼將運行,調用 handleClick(0)。

更新界面

通常你會希望你的組件 “記住” 一些信息并展示出來,比如一個按鈕被點擊的次數。要做到這一點,你需要在你的組件中添加 state

首先,從 React 引入 useState

import { useState } from 'react';

現在你可以在你的組件中聲明一個 state 變量:

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

你將從useState 中獲得兩樣東西:當前的state(count),以及用于更新它的函數(setCount)。你可以給它們起任何名字,但按照慣例會像 [something, setSomething] 這樣為它們命名。

第一次顯示按鈕時,count的值為 0,因為你把 0 傳給了 useState()。當你想改變state 時,調用 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。繼續點擊會逐步遞增。
如果你多次渲染同一個組件,每個組件都會擁有自己的 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,而不影響其他按鈕。

使用 Hook

use 開頭的函數被稱為 HookuseState 是 React 提供的一個內置 Hook。你可以在 React API 參考 中找到其他內置的 Hook。你也可以通過組合現有的 Hook 來編寫屬于你自己的 Hook。

Hook 比普通函數更為嚴格。你只能在你的組件(或其他 Hook)的 頂層 調用 Hook。如果你想在一個條件或循環中使用 useState,請提取一個新的組件并在組件內部使用它。

組件間共享數據

前面的示例中,每個 MyButton 都有自己獨立的 count,當每個按鈕被點擊時,只有被點擊按鈕的 count 才會發生改變:

然而,你經常需要組件 共享數據并一起更新。

為了使得 MyButton 組件顯示相同的 count 并一起更新,你需要將各個按鈕的 state “向上” 移動到最接近包含所有按鈕的組件之中。

在這個示例中,它是 MyApp:

此刻,當你點擊任何一個按鈕時,MyApp 中的 count 都將改變,同時會改變 MyButton 中的兩個 count。具體代碼如下:

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 一同向下傳遞到 每個 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>
  );
}

使用這種方式傳遞的信息被稱作 prop。此時 MyApp 組件包含了 count state 以及 handleClick 事件處理函數,并將它們作為 prop 傳遞給 了每個按鈕。

最后,改變 MyButton 以 讀取 從父組件傳遞來的 prop:

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

當你點擊按鈕時,onClick 處理程序會啟動。每個按鈕的 onClick prop 會被設置為 MyApp 內的 handleClick 函數,所以函數內的代碼會被執行。該代碼會調用 setCount(count + 1),使得 state 變量 count 遞增。新的 count 值會被作為 prop 傳遞給每個按鈕,因此它們每次展示的都是最新的值。這被稱為 狀態提升。通過向上移動 state,我們實現了在組件間共享它。

要從多個子組件收集數據,或讓兩個子組件相互通信,請改為在其父組件中聲明共享 state。父組件可以通過 props 將該 state 傳回給子組件。這使子組件彼此同步并與其父組件保持同步。

實戰

教程:井字游戲 力推!,對于初學react的開發人員,還是具有很大意義。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容