v17.0.2
React 是什麼?
React 是一個陳述式、高效且具有彈性的 JavaScript 函式庫,用以建立使用者介面。
它讓你使用小巧而獨立的 component
,來建立複雜的 UI。
開發環境
-
安裝
若要開發標準的 React 專案,建議使用
create-react-app
來建立新專案。npm i -g create-react-app
-
建立新專案
create-react-app <專案名稱>
主要概念
-
Hello World
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
); -
JSX 介紹
JSX
是一種 JavaScript 的擴充語言,加入了一些 HTML 標籤的語法。
React
架構在設計上將 HTML 標籤與 JavaScript 控制邏輯合併,以JSX
來描述 UI 的外觀與運作邏輯,
打造出 React 的 UI 組件(components),再用這些 UI 組件堆疊出個應用程式。-
在 JSX 中嵌入 Expression
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);function formatName(user) {
return user.firstName+ ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
ReactDOM.render(
element,
document.getElementById('root')
); -
JSX 本身也是 Expression
使用 JSX 作為參數並由 function 中回傳。
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
} -
在 JSX 中指定屬性
-
可以使用引號將字串設定為屬性:
const element = <div tabIndex="0"></div>;
-
也可以在屬性中使用大括號來嵌入一個 JavaScript expression:
const element = <img src={user.avatarUrl}></img>;
不要在嵌入 JavaScript expression 作為屬性的時候同時使用引號或是大括號。
React DOM 使用
camelCase
來命名屬性而不是使用慣有的 HTML 屬性名稱。
舉例來說:在 JSX 之中,class
變成了 className 而tabindex
變成了 tabIndex。 -
在 JSX 中指定 Children
const element = <img src={user.avatarUrl} />;
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
); -
JSX 表示物件
const element = (
<h1 className="greeting">
Hello, World!
</h1>
);Babel 將 JSX 編譯為呼叫
React.createElement()
的程式。const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, World!'
); -
-
-
Render Element
建立 React 應用程式最小的單位是 element。
與瀏覽器的 DOM element 不同,
React element
是單純的 object,而且很容易被建立。
React DOM
負責更新 DOM 來符合 React element。-
Render Element 到 DOM內
使用 React 建立應用程式時,通常會有一個單一的 root DOM node。<div id="root"></div>
如果想要整合 React 到現有的應用程式時,可以根據需求獨立出多個 root DOM node。
如果要 render 一個 React element 到 root DOM node,傳入兩者到ReactDOM.render()
。const element = <h1>Hello, world</h1>;
ReactDOM.render(
element,
document.getElementById('root')
); -
更新被 Render 的 Element
React element 是不可變動性
的。
一旦你建立一個 element,你不能改變它的 children 或是 attribute。
更新 UI 唯一的方式是建立一個新的 element,並且將它傳入到ReactDOM.render()
。function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
// 從 setInterval() callback 每秒呼叫 ReactDOM.render()。 -
React 只更新必要的 Element
React DOM 會將 element 和它的 children 與先前的狀態做比較,
並且只更新必要的 DOM 達到理想的狀態。
只有內容更改的 node 才會被 React DOM 更新。
-
-
Components 與 Props
概念上來說,component 就像是 JavaScript 的 function,
它接收任意的參數(稱之為「props」)並且回傳描述畫面的 React element。-
Function Component 與 Class Component
定義 component 最簡單的方法即是撰寫一個 Javascript function:也可以使用 ES6 Class 來定義 component:function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
} -
Render 一個 Component
React element 也可以是使用者自定義的 component:
當 React 看到由使用者定義 component 的 element 時,
它將 JSX 屬性和 children 作為 single object 傳遞給該 component。
我們稱這個 object 為「props」。React 以function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);{name: 'Sara'}
作為 props 傳入 Welcome component 並呼叫。
Welcome component 回傳了<h1>Hello, Sara</h1>
這個 element 作為返回值。Component 的字首須為大寫字母。
React 會將小寫字母開頭的組件視為原始 DOM 標籤。 -
組合 Component
Component 可以在輸出中引用其他 component。
可以在任何層次中抽象化相同的 component,按鈕、表單、對話框、甚至是整個畫面,
在 React 應用程式中都將以 component 的方式呈現。通常來說,每個 React 應用程式都有一個最高層級的 App component。function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
然而,將 React 結合至現存的應用程式中,可能需要使用像 Button 這樣的小型 component,
並由下往上,逐步應用到畫面的最高層級。 -
Props 是唯讀的
不管你使用function
或是class
來宣告 component,
都絕不能修改自己的 props。
所有的 React component 都必須像 Pure function 一般保護他的 props。
-
-
State 和生命週期
封裝 Clock component 讓它可以真正的被重複使用。
它將會設定本身的 timer 並且每秒更新一次。function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('root')
);
}
setInterval(tick, 1000);理想情況下,Clock 會自己更新,需要加入
state
到 Clock component。
State 類似於 prop,但它是私有且由 component 完全控制的。-
轉換 Function 成 Class
透過以下 5 個步驟轉換一個 function component 像是 Clock 成為 class:
- 建立一個相同名稱並且繼承
React.Component
的 ES6 class。 - 加入一個
render()
的空方法。 - 將 function 的內容搬到
render()
方法。 - 將
render()
內的props
替換成this.props
。 - 刪除剩下空的 function 宣告。
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
} - 建立一個相同名稱並且繼承
-
加入 Local State 到 Class
透過以下 3 個步驟將 date 從搬移到 state:
- 將
render()
方法內的this.props.date
替換成this.state.date
: - 加入一個 class constructor 並分配初始的
this.state
: - 從
<Clock />
element 中移除date
prop:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
); - 將
-
加入生命週期方法到 Class
每當
Clock
render 到 DOM 的時候,我們想要設定一個timer
。在 React 中稱為「mount」。
每當產生的Clock
DOM 被移除時,我們想要清除timer
。在 React 中稱為「unmount」。
這些方法被稱為「生命週期方法」
。
componentDidMount()
方法會在 component 被 render 到 DOM 之後才會執行。
在componentWillUnmount()
生命週期方法內移除 timer。class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
); -
正確的使用 State
-
請不要直接修改 State
// 錯誤
// 唯一可以指定 `this.state` 值的地方是在 constructor。
this.state.comment = 'Hello';
// 正確
this.setState({comment: 'Hello'}); -
State 的更新可能是非同步的
React 可以將多個setState()
呼叫批次處理為單一的更新,以提高效能。
this.props
和this.state
可能是非同步的被更新,你不應該依賴它們的值來計算新的 state。// 錯誤
this.setState({
counter: this.state.counter + this.props.increment,
});
// 正確
this.setState((state, props) => ({
counter: state.counter + props.increment
})); -
State 的更新將會被 Merge
呼叫setState()
時,React 會 merge object 到目前的 state。constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
-
-
向下資料流
Component 可以選擇將它的 state 做為 props 往下傳遞到它的 child component:
<FormattedDate date={this.state.date} />
FormattedDate component 會在它的 props 接收到 date
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}任何 state 總是由某個特定的 component 所擁有,
任何從 state 得到的資料或 UI,state 只能影響在 tree「以下」的 component。
-
-
事件處理
- 事件的名稱在 React 中都是
camelCase
,而在 HTML DOM 中則是小寫。 - 事件的值在 JSX 中是一個
function
,而在 HTML DOM 中則是一個 string。
在 HTML 中的語法:<button onclick="activateLasers()">
Activate Lasers
</button>
在 React 中的語法:
<button onClick={activateLasers}>
Activate Lasers
</button>在 React 中,不能夠使用 return false 來避免瀏覽器預設行為。
必須明確地呼叫preventDefault
。function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}請特別注意
this
在 JSX callback 中的意義。
在 JavaScript 中,class 的方法在預設上是沒有被綁定(bound
)的。
把它傳遞給 onClick 的話,this 的值將會在該 function 被呼叫時變成 undefined。class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 為了讓 `this` 能在 callback 中被使用,這裡的綁定是必要的:
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);-
將參數傳給 Event Handler
在一個迴圈中,我們常常會需要傳遞一個額外的參數給 event handler。
如果 id 是每一行的 ID 的話,下面兩種語法都可行:以這兩個例子來說,<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>e
這個參數所代表的 React 事件將會被當作 ID 之後的第二個參數被傳遞下去。
在使用 arrow function 時,我們必須明確地將它傳遞下去,
但若使用 bind 語法,未來任何的參數都將會自動被傳遞下去。
- 事件的名稱在 React 中都是
-
條件 Render
使用 JavaScript 中的運算子如
if
或者三元運算子
來建立表示目前 state 的 element,
然後讓 React 根據它們來更新 UI。function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
// 建立一個 Greeting component,它會根據使用者是否已登入來顯示其中之一:
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
// 根據 isLoggedIn prop 的值來 render 不同的問候語。
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);-
Element 變數
可以用變數來儲存 element。
function LoginButton(props) {
return (
<button onClick={props.onClick}>
Login
</button>
);
}
function LogoutButton(props) {
return (
<button onClick={props.onClick}>
Logout
</button>
);
}
// 建立一個名為 LoginControl 的 stateful component。
// 它將根據目前的 state 來 render <LoginButton /> 或 < LogoutButton />。
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
); -
Inline If 與 && 邏輯運算子
透過大括號在 JSX 中嵌入表達式,包括 JavaScript 的 && 邏輯運算子,
可以方便 render 有條件的 element:function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('root')
);能夠這樣做是因為在 JavaScript 中,
true && expression
總是回傳expression
,
而false && expression
總是回傳false
。所以,當條件為
true
時,&&
右側的 element 會出現在輸出中,如果是false
,React 會忽略並跳過它。請注意,回傳 falsy expression 仍會導致
&&
之後的 element 被忽略,
但依舊回傳 falsy expression,在下面的範例中,render 將會回傳<div>0</div>
。render() {
const count = 0;
return (
<div>
{ count && <h1>Messages: {count}</h1>}
</div>
);
} -
Inline If-Else 與三元運算子
另一個有條件 render element 的方式是透過 JavaScript 的三元運算子
condition ? true : false
。render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
} -
防止 Component Render
在少數的情況下,可能希望 component 隱藏自己本身,即便它是由另一個 component 被 render。
可以透過回傳null
而不是它的 render 輸出。在下面的範例中,
<WarningBanner />
的 render 取決於warn
prop 的值。
如果 prop 是false
,它就不會 render。function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true};
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? 'Hide' : 'Show'}
</button>
</div>
);
}
}
ReactDOM.render(
<Page />,
document.getElementById('root')
);在 component 中回傳 null 並不會影響 component 的生命週期方法。
例如 componentDidUpdate 依然可以被呼叫。
-
-
列表與Key
-
Render 多個 Component
可以建立一系列的 element 並用大括號
{}
將它們包含在 JSX 裡面
。
用 JavaScript 的 map() function 迭代numbers
array,我們每次都會回傳一個<li>
element。
最後,我們會把結果產生的 element array 設定為listItems
:const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);把整個
listItems
array 包含在一個<ul>
element 裡,然後render 到 DOM 上面
:ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('root')
); -
基本列表 Component
將上面的範例改寫為一個接收
numbers
array 並輸出一個沒有排序的 element 列表的 component。function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);當執行這段程式碼時,會收到一個關於應該提供 key 給每一個列表項目的警告。
「key」是當你在建立一個 element 列表時必須使用的特殊的 string attribute。為 numbers.map() 列表中的每個項目分配一個 key,並修正遺漏 key 的問題。
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
); -
Key
Key 幫助 React 分辨哪些項目被改變、增加或刪除。
在 array 裡面的每個 element 都應該要有一個 key,如此才能給予每個 element 一個固定的身份:const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);選擇 key 最佳的方法是在列表中使用唯一識別字串來區別 sibling 項目。
通常,會使用資料的 ID 作為 key:const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);當 render 的項目沒有固定的 ID ,且也沒有更好的辦法時,
可以使用項目的索引做為 key:// 請在項目沒有固定的 ID 時才這樣做
const todoItems = todos.map((todo, index) =>
<li key={index}>
{todo.text}
</li>
);不建議使用索引作為 key,尤其如果項目的順序會改變的話。
這會對效能產生不好的影響,也可能會讓 component state 產生問題。 -
用 Key 抽離 Component
Key 只有在周遭有 array 的情境中才有意義。
例如,如果要抽離一個 ListItem component 的話,應該把 key 放在 array 裡的element 上,
而不是把它放在 ListItem 裡面的 - element 上。
範例:Key 的錯誤使用方式
function ListItem(props) {
const value = props.value;
return (
// 錯!你不需要在這裡指出 key:
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem value={number} /> // 應該要在這裡指出 key:
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);範例:Key 的正確使用方式
function ListItem(props) {
// 不需要在這裡指出 key:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()} value={number} /> // 正確!Key 應該在 array 內被指定。
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);一個好的經驗法則是,在
map()
呼叫中的每個 element 都會需要 key。 -
Key 必須在 Sibling 中是唯一的
在 array 中使用的 key 應該要是唯一的值。然而,它們不必在全域中唯一。
當產生兩個不同的 array 時,仍然可以使用相同的 key:function Blog(props) {
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}
const posts = [
{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById('root')
);Key 的功能是提示 React,但它們不會被傳遞到你的 component。
如果在 component 中需要同樣的值,可以直接把這個值用一個不同的名稱作為 prop 傳下去:const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);Post
component 可以讀取props.id
,但不能讀取props.key
。 -
在 JSX 中嵌入 map()
在上面的例子中,我們宣告了另一個
listItems
變數並把它包含在 JSX 中:function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()} value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}JSX 能在大括號中
嵌入任何表達式
,所以能夠 inlinemap()
的結果:function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()} value={number} />
)}
</ul>
);
}有時候這會產生更乾淨的程式碼,但這種風格也可能被濫用。
就像 JavaScript 一樣,是否要將變數抽取出來以增加可讀性完全是看你的決定。
請記得,如果map()
的程式碼層級變得過度巢狀,也許就是使用抽離component
的時候了。
-
-
表單
-
Controlled Component
在 HTML 中,表單的 element 像是
<input>
、<textarea>
和<select>
通常會維持它們自身的 state,並根據使用者的輸入來更新 state。
在 React 中,可變的 state 通常是被維持在 component 中的 state property,並只能以 setState() 來更新。
render 表單的 React component 同時也掌握了後續使用者的輸入對表單帶來的改變。
像這樣一個輸入表單的 element,被 React 用這樣的方式來控制它的值,就被稱為「controlled component」。class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}由於
value
attribute 是被設定在表單 element 上,顯示的 value 會永遠是this.state.value
,這使得 React 的 state 成為了資料來源。
由於handleChange
在每一次鍵盤被敲擊時都會被執行,並更新 React 的 state,因此被顯示的 value 將會在使用者打字的同時被更新。在這樣的 controlled component 中,顯示的 value 始終由 React 的 state 驅動,
雖然這意味著必須寫更多的 code,但現在同時可以將 value 傳遞給其他的 UI element,或是從其他 event handler 重置。 -
Textarea 標籤
在 HTML 中,一個
<textarea>
的 element 是經由它的 children 來定義它的文字:<textarea>
Hello there, this is some text in a text area
</textarea>在 React 中,
<textarea>
則是使用一個value
的 attribute。
如此一來,一個使用<textarea>
的表單可以使用非常類似單行的 input 方法來寫成:class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Please write an essay about your favorite DOM element.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('An essay was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Essay:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
} -
Select 標籤
在 HTML 中,
<select>
會建立一個下拉式選單。
例如,這個 HTML 會建立一個有各種水果的下拉式選單:<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>椰子的選項是一開始就被選定的,因為它有一個
selected
attribute。
但是在 React 中並不是用selected
attribute,而是在select
的標籤上用一個value
attribute。
對一個 controlled component 來說這是比較方便的,因為只需要在一個地方更新它。例如:class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Pick your favorite flavor:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}整體來說,這使得
<input type="text">
,<textarea>
和<select>
使用起來都很類似。
它們全都會接收一個 controlled component 時會使用到的value
attribute。 -
檔案 input 標籤
在 HTML 中,
<input type="file">
讓使用者從它們的儲存裝置中選擇一個至多個檔案,
並把它們上傳到伺服器或透過File API
被 JavaScript 處理。<input type="file" />
由於它的值是唯讀,它在 React 中是一個 uncontrolled component。
-
處理多個輸入
當需要處理多個 controlled
input
element,可以在每個 element 中加入一個name
attribute,
並讓 handler function 選擇基於event.target.name
的值該怎麼做:
-
參考資源: