創建和嵌套組件
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}
會讀取 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
循環和 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
開頭的函數被稱為 Hook。useState
是 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的開發人員,還是具有很大意義。