React 快速上手
開始
- 開始一個
React
的項目.
npx create-react-app my-app
注意:
Node >= 8.10
和npm >= 5.6
,npx
是 npm 5.2+ 附帶的 package 運行工具。
參數說明:
my-app
: 指的是項目路徑,比如想在當前目錄創建項目,則npx create-react-app .
過程可能會久一點,安裝完之後,你會發現git
已經是創建好的.npm start
測試安裝是否成功:
如果有以上信息,則成功,默認端口號是3000
,按照提示我們訪問,http://localhost:3000,一般會自動訪問.
- 從
hello world
開始.
程序主入口是src/index.js
.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
其中關鍵的是, ReactDOM.render();
以上含義是將APP
組件掛載到root
上.那麼這個root
,又是哪裏的.
到public\index.html
文件下有:
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
發現在該html
文件下, <div id="root"></div>
, 所以其實是讓該組件掛載到該組件下.
所以我們可以修改掛載的內容.
ReactDOM.render(<h2>hello world</h2>, document.getElementById('root'));
再次運行應該就能看到 hello world
, 訪問http://localhost:3000.
瞭解JSX
它是一個 JavaScript 的語法擴展.它跟模板語言有點類似, 作用當然是讓視圖與數據進行分離, 從而使得組件進行復用.JSX
可以生成 React
“元素”.
示例: 對於剛纔的 hello world
,我們就可以利用.
const name = 'Veng';
const element = <h2>hello ,{name}</h2>;
ReactDOM.render(element, document.getElementById('root'));
這樣就可以對視圖和數據及進行分離, 可以動態的生成視圖,變量用花括號{}
括起來,並且這樣模式是防注入攻擊的, 就算是用戶輸入, 它會將輸入內容轉化爲字符串解析.
實際上, 最好的做法是將組件單獨成一個模塊,然後再導入,跟之前原始的<App/>
一樣.
注意:
要使用JSX,必須導入React才能夠用.
import React from 'react';
元素渲染
什麼是元素, 就像剛剛的 const element = <h2>hello ,{name}</h2>;
就是元素.與瀏覽器的 DOM 元素不同,React 元素是創建開銷極小的普通對象。React DOM 會負責更新 DOM 來與 React 元素保持一致。
注意:
你可能會將元素與另一個被熟知的概念——“組件”混淆起來。組件是由元素構成的。
更新已渲染的元素.
React 元素是不可變對象。一旦被創建,你就無法更改它的子元素或者屬性。一個元素就像電影的單幀:它代表了某個特定時刻的 UI。
根據我們已有的知識,更新 UI 唯一的方式是創建一個全新的元素,並將其傳入 ReactDOM.render()
。
示例: 時間顯示器
function tick() {
const element = (
<div>
<h2>it is {new Date().toLocaleString()}</h2>
</div>);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
這個例子會在 setInterval()
回調函數,每秒都調用 ReactDOM.render()
.
在實踐中,大多數 React 應用只會調用一次
ReactDOM.render()
.
上面的例子中, 雖然我們對整個頁面進行更新,但是實際上React
只進行了局部更新.React DOM
會將元素和它的子元素與它們之前的狀態進行比較,並只會進行必要的更新來使 DOM
達到預期的狀態。
爲了驗證渲染的部分,可以觀察 DOM
結構:
但是實際上, 我們需要的並不是UI
隨時間的變化而變化, 而是在在任意時刻給定狀態. 由此就有了組件.
組件 props
組件,從概念上類似於 JavaScript 函數。它接受任意的入參(即 “props”),並返回用於描述頁面展示內容的 React 元素。
定義組件
- 開始定義第一個組件
src/components/Welcome.js
,有兩種方式創建組件一種是通過函數,一種是通過class
.
import React from 'react';
function Welcome(props) {
return (<h2>hello,{props.name}</h2>);
}
export default Welcome;
import React from 'react';
class Welcome extends React.Component{
render(){
return <h2>hello,{this.props.name}</h2>;
}
}
export default Welcome;
每次組件更新時 render
方法都會被調用.
src/index.js
,引入組件,並導入數據.
import Welcome from './components/Welcome';
ReactDOM.render(<Welcome name="Veng"/>, document.getElementById('root'));
複用組件
從組件的構成可以看出,各自組件的數據是互不影響的.
function App() {
return (
<div>
<Welcome name="A" />
<Welcome name="B" />
<Welcome name="C" />
</div>
);
}
通常來說,每個新的 React 應用程序的頂層組件都是
App
組件。但是,如果你將 React 集成到現有的應用程序中,你可能需要使用像Button
這樣的小組件,並自下而上地將這類組件逐步應用到視圖層的每一處。
總得來說,就是組件的複用程度越高越好,這樣也就更加的靈活.
組件無論是使用函數聲明還是通過 class 聲明,都決不能修改自身的 props
那麼爲什麼自身的props
不能被修改? 因爲如果出現這樣情況,子組件的props
是來自父組件,那麼如果自組件的props
值改變了,父組件也會改變.實際上,我們不能讓這種情況發生.故而有了一個保存組件狀態值的一個參數state
,props
僅僅用來初始化,state
用來保存組件的狀態,往子組件傳遞數據時用的是state
,子組件用props
接收,用自身得state
保存狀態,這樣子遞歸,每個組件的狀態state
都是私有得.
State 和 生命週期
之前的時間顯示,是每次都需要Render
,我們希望,編寫一次代碼,便可以讓時間組件自我更新:
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
我們需要在 Clock
組件中添加 “state” 來實現這個功能。
State 與 props 類似,但是 state 是私有的,並且完全受控於當前組件。
- 先將時間信息放在
state
裏.
import React from 'react';
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date() };
}
render() {
return (
<div>
<h2>it is {this.state.date.toLocaleString()}</h2>
</div>
);
}
}
export default Clock;
constructor
是構造器,初始化狀態.
這樣子寫了之後,時間並沒自動更新,所以怎麼讓state.date
更新? 同樣得,我們可以設置一個計時器來進行對其更新.
那麼計時器在哪裏設置. 組件提供了第一次渲染時的函數componentDidMount
,在React
稱之爲掛載 mount
,還有組件被刪除時的函數componentWillUnmount
,在React
稱之爲卸載 unmount
.
import React from "react";
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date() };
}
componentDidMount() {
this.timerId = setInterval(() => {
this.setState({
date: new Date()
});
}, 1000);
}
componentWillUnmount() {
clearInterval(this.timerId);
}
render() {
return (
<div>
<h2>it is {this.state.date.toLocaleString()}</h2>
</div>
);
}
}
export default Clock;
快速概括一下發生了什麼和這些方法的調用順序:
- 當
<Clock />
被傳給ReactDOM.render()
的時候,React 會調用Clock
組件的構造函數。因爲Clock
需要顯示當前的時間,所以它會用一個包含當前時間的對象來初始化this.state
。我們會在之後更新 state。- 之後 React 會調用組件的
render()
方法。這就是 React 確定該在頁面上展示什麼的方式。然後 React 更新 DOM 來匹配Clock
渲染的輸出。- 當
Clock
的輸出被插入到 DOM 中後,React 就會調用ComponentDidMount()
生命週期方法。在這個方法中,Clock
組件向瀏覽器請求設置一個計時器來每秒調用setState()
。Clock
組件會通過調用setState()
來計劃進行一次 UI 更新。得益於setState()
的調用,React 能夠知道 state 已經改變了,然後會重新調用render()
方法來確定頁面上該顯示什麼。這一次,render()
方法中的this.state.date
就不一樣了,如此以來就會渲染輸出更新過的時間。React 也會相應的更新 DOM。- 一旦
Clock
組件從 DOM 中被移除,React 就會調用componentWillUnmount()
生命週期方法,這樣計時器就停止了
關於 setState()
:
爲什麼不直接修改:this.state.date=new Date()
? 代碼沒問題,但是這樣子修改並不會通知組件,state
變了,所以構造函數是唯一可以給this.state
賦值的地方.
出於性能考慮,React 可能會把多個
setState()
調用合併成一個調用。因爲
this.props
和this.state
可能會異步更新,所以你不要依賴他們的值來更新下一個狀態。
例如,此代碼可能會無法更新計數器:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
可能當前組件的通知排在前面,而後面又改變了,但是排在了後面.
要解決這個問題,可以讓setState()
接收一個函數而不是一個對象,這樣渲染得時候保存是一個函數,就會回調.
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
state 的存在,數據是自上往下流動的.
這個函數用上一個 state 作爲第一個參數,將此次更新被應用時的 props 做爲第二個參數,順序記住.
事件處理
React 元素的事件處理和 DOM 元素的很相似,但是有一點語法上的不同:
- React 事件的命名採用小駝峯式(camelCase),而不是純小寫。
- 使用 JSX 語法時你需要傳入一個函數作爲事件處理函數,而不是一個字符串。
示例:
傳統dom
<button onclick="activateLasers()">
Activate Lasers
</button>
react:
<button onClick={activateLasers}>
Activate Lasers
</button>
在 React 中另一個不同點是你不能通過返回
false
的方式阻止默認行爲。你必須顯式的使用preventDefault
.
示例:
傳統的 HTML 中阻止鏈接默認打開一個新頁面,你可以這樣寫:
<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>
React 中,可能是這樣的:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
這樣a
標籤的默認點擊事件不會觸發,頁面不會跳轉,這就是preventDefault
的作用.
在這裏,e
是一個合成事件。React 根據 W3C 規範來定義這些合成事件,所以你不需要擔心跨瀏覽器的兼容性問題。
使用 React 時,你一般不需要使用 addEventListener
爲已創建的 DOM 元素添加監聽器。事實上,你只需要在該元素初始渲染的時候添加監聽器即可。
當你使用 ES6 class 語法定義一個組件的時候,通常的做法是將事件處理函數聲明爲 class 中的方法.
import React from 'react';
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = { isToggle: true };
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(state => ({
isToggle: !state.isToggle,
}));
}
render() {
return (
<div>
<button onClick={this.handleToggleClick}>
{this.state.isToggle ? 'ON' : 'OFF'}
</button>
</div>
);
}
}
export default Toggle;
其中的處理點擊事件的方法,我們在初始化的時候給其綁定了thisthis.handleToggleClick = this.handleToggleClick.bind(this);
,使得函數裏的this
指向組件,否則函數裏的this
是```undefined,你必須謹慎對待 JSX 回調函數中的
this,在 JavaScript 中,class 的方法默認不會綁定
this`。
如果你覺得bind
這樣麻煩,我們可以利用箭頭函數.
handleToggleClick = () => {
this.setState(state => ({
isToggle: !state.isToggle,
}));
}
這樣就不用bind
了,這裏的this
會指向組件.
亦或者:
render() {
return (
<div>
<button onClick={e=>this.handleToggleClick(e)}>
{this.state.isToggle ? 'ON' : 'OFF'}
</button>
</div>
);
}
這樣也是可行的,不過以上推薦第二種.
條件渲染
在 React 中,你可以創建不同的組件來封裝各種你需要的行爲。然後,依據應用的不同狀態,你可以只渲染對應狀態下的部分內容。
示例:
src\components\Greeting.js
import React from 'react';
function GuestGreeting() {
return <h1>Please sign in!</h1>
}
function UserGreeting() {
return <h1>Welcome back!</h1>
}
function Greeting(props) {
const { isLogin } = props;
return isLogin ? <UserGreeting /> : <GuestGreeting />;
}
export default Greeting;
src\index.js
:
import Greeting from './components/Greeting';
ReactDOM.render(<Greeting isLogin={false} />, document.getElementById('root'));
這裏組件會根據登錄狀況來返回不同的問候語.
複雜一點條件: 添加登錄登出按鈕.
import React from 'react';
import Greeting from './Greeting';
class GreetingWithButton extends React.Component {
constructor(props) {
super(props);
this.state = { isLogin: props.isLogin };
}
handleLoginClick = () => {
this.setState({
isLogin: true,
});
}
handleLogoutClick = () => {
this.setState({
isLogin: false,
});
}
render() {
const { isLogin } = this.state;
const button = isLogin ? <button onClick={this.handleLogoutClick}>Logout</button>
: <button onClick={this.handleLoginClick}>Login</button>
return (
<div>
<Greeting isLogin={this.state.isLogin} />
{button}
</div>
);
}
}
export default GreetingWithButton;
阻止渲染
在極少數情況下,你可能希望能隱藏組件,即使它已經被其他組件渲染。若要完成此操作,你可以讓 render
方法直接返回 null
,而不進行任何渲染。
示例:顯示隱藏警告!
src\components\Warning.js
:
import React from 'react';
class Warning extends React.Component {
render() {
const { warn } = this.props;
return warn ? (
<div className='warning'>
Warning!
</div>
) : null;
}
}
export default Warning;
src\components\Page.js
:
import React from 'react';
import Warning from './Warning';
class Page extends React.Component {
constructor(props) {
super(props);
this.state = { showWarning: false };
}
handleClick = () => {
this.setState(state => ({
showWarning: !state.showWarning
}));
}
render() {
const showWarning = this.state.showWarning;
return (
<div>
<Warning warn={showWarning} />
<button onClick={this.handleClick}>{showWarning ? 'Hide' : 'Show'}</button>
</div>
);
}
}
export default Page;
src\index.js
:
import Page from './components/Page';
ReactDOM.render(<Page />, document.getElementById('root'));
在組件的
render
方法中返回null
並不會影響組件的生命週期.
列表 和 key
利用map
函數.
import React from 'react';
class NumberList extends React.Component {
render() {
const { numbers } = this.props;
const listItems = numbers.map(number => {
return <li>{number}</li>
});
return <ul>{listItems}</ul>
}
}
export default NumberList;
將該組件掛載到root
上面,會發現有一個warning
:
意思是當你創建一個元素時,必須包括一個特殊的 key
屬性。
按照它的警告我們爲每個元素加上:
const listItems = numbers.map(number => {
return <li key={number}>{number}</li>
});
這樣警告消除了.
那麼Key
的作用是什麼呢?
Key
key 幫助 React 識別哪些元素改變了,比如被添加或刪除。因此你應當給數組中的每一個元素賦予一個確定的標識。
一個元素的 key 最好是這個元素在列表中擁有的一個獨一無二的字符串。通常,我們使用數據中的 id 來作爲元素的 key.
數組元素中使用的 key 在其兄弟節點之間應該是獨一無二的。然而,它們不需要是全局唯一的。當我們生成兩個不同的數組時,我們可以使用相同的 key 值.
當元素沒有確定 id 的時候,萬不得已你可以使用元素索引 index 作爲 key:
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
如果列表項目的順序可能會變化,我們不建議使用索引來用作 key 值,因爲這樣做會導致性能變差,還可能引起組件狀態的問題。
如果你選擇不指定顯式的 key 值,那麼 React 將默認使用索引用作爲列表項目的 key 值。
元素的 key 只有放在就近的數組上下文中才有意義。
示例:
function ListItem(props) {
// 正確!這裏不需要指定 key:
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
// 正確!key 應該在數組的上下文中被指定
<ListItem key={number.toString()}
value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
這裏的key
是放在ListItem
上而不是li
上,因爲就近數組的上下文在ListItem
這裏.
一個好的經驗法則是:在
map()
方法中的元素需要設置 key 屬性。
還有就是對於key屬性,它不會傳值給組件.
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
Post
組件的props
讀不到key
,但是可以讀取到props.id
和props.title
.
表單
在 React 裏,HTML 表單元素的工作方式和其他的 DOM 元素有些不同,這是因爲表單元素通常會保持一些內部的 state。
在 HTML 中,表單元素(如<input>
、 <textarea>
和 <select>
)之類的表單元素通常自己維護 state,並根據用戶輸入進行更新。而在 React 中,可變狀態(mutable state)通常保存在組件的 state 屬性中,並且只能通過使用 setState()
來更新。我們可以把兩者結合起來,使 React 的 state 成爲“唯一數據源”。渲染表單的 React 組件還控制着用戶輸入過程中表單發生的操作。被 React 以這種方式控制取值的表單輸入元素就叫做“受控組件”。
示例:
import React from 'react';
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: '' };
}
handleChange = (e) => {
this.setState({
value: e.target.value,
});
}
handleSubmit = (e) => {
console.log(this.state.value);
e.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input value={this.state.value} onChange={this.handleChange} />
<input type='submit' value='提交' />
</form>
);
}
}
export default NameForm;
以上將input
的值綁定在了該組件的state
.
處理多個輸入
import React from 'react';
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
}
handleInputChange = (event) => {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
參與:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
來賓人數:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
export default Reservation;
這個例子中,兩個input
綁定的同一個函數,函數中通過name
來獲悉是哪個組件,並且name
與state
裏的屬性名相同.
狀態的提升
現在是有這麼個需求,有兩個輸入框,一個輸入框是攝氏度,一個是華氏度,只要輸入的溫度,兩個框會自動同步轉換,也就是兄弟組件之間怎麼傳遞數據的問題.
先寫溫度輸入框組件:
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {temperature: ''};
}
handleChange(e) {
this.setState({temperature: e.target.value});
}
render() {
const temperature = this.state.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
父組件:
class Calculator extends React.Component {
render() {
return (
<div>
<TemperatureInput scale="c" />
<TemperatureInput scale="f" />
</div>
);
}
}
但是有一個問題,兩個溫度輸入框的溫度都保存在自己的state
裏,兩者怎麼進行數據的交互?
解決方案是,將溫度的接收提升到父組件,也就是說溫度輸入框的數據,是由父組件傳遞過來的props
決定.
溫度輸入框組件修改爲:
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onTemperatureChange(e.target.value);
}
render() {
const temperature = this.props.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
現在輸入框的數據由父組件傳入所決定,並且onChange
綁定調用的是有父組件傳遞的函數.
父組件修改爲:
// 溫度轉換的函數
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
function tryConvert(temperature, convert) {
const input = parseFloat(temperature);
if (Number.isNaN(input)) {
return '';
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
// 父組件
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}
handleCelsiusChange(temperature) {
this.setState({scale: 'c', temperature});
}
handleFahrenheitChange(temperature) {
this.setState({scale: 'f', temperature});
}
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} />
</div>
);
}
}
兩個輸入框的值由父組件完全掌控,解決兄弟節點數據問題,可以提升到父組件來解決.
在 React 應用中,任何可變數據應當只有一個相對應的唯一“數據源”。通常,state 都是首先添加到需要渲染數據的組件中去。然後,如果其他組件也需要這個 state,那麼你可以將它提升至這些組件的最近共同父組件中。你應當依靠自上而下的數據流,而不是嘗試在不同組件間同步 state。
組合和繼承
React 有十分強大的組合模式。我們推薦使用組合而非繼承來實現組件間的代碼重用。
包含關係
有些組件無法提前知曉它們子組件的具體內容。在 Sidebar
(側邊欄)和 Dialog
(對話框)等展現通用容器(box)的組件中特別容易遇到這種情況。
我們建議這些組件使用一個特殊的 children
prop 來將他們的子組件傳遞到渲染結果中:
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
這使得別的組件可以通過 JSX 嵌套,將任意組件作爲子組件傳遞給它們。
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
FancyBorder
組件中的內容是通過父組件插進來的,這就是props.children
的作用,還是十分的實用,實際上我們也能自定義一個屬性通過props
傳遞,當然沒問題,但是有children
提供了,可以減少代碼量.
但是如果碰到需要補充多個部分內容,顯然一個children
只能補充一個地方,所以此時我們可以自定義屬性傳遞.
繼承
在 Facebook,我們在成百上千個組件中使用 React。我們並沒有發現需要使用繼承來構建組件層次的情況。
Props 和組合爲你提供了清晰而安全地定製組件外觀和行爲的靈活方式。注意:組件可以接受任意 props,包括基本數據類型,React 元素以及函數。
如果你想要在組件間複用非 UI 的功能,我們建議將其提取爲一個單獨的 JavaScript 模塊,如函數、對象或者類。組件可以直接引入(import)而無需通過 extend 繼承它們。
特殊的 children
prop 來將他們的子組件傳遞到渲染結果中:
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
這使得別的組件可以通過 JSX 嵌套,將任意組件作爲子組件傳遞給它們。
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
FancyBorder
組件中的內容是通過父組件插進來的,這就是props.children
的作用,還是十分的實用,實際上我們也能自定義一個屬性通過props
傳遞,當然沒問題,但是有children
提供了,可以減少代碼量.
但是如果碰到需要補充多個部分內容,顯然一個children
只能補充一個地方,所以此時我們可以自定義屬性傳遞.
繼承
在 Facebook,我們在成百上千個組件中使用 React。我們並沒有發現需要使用繼承來構建組件層次的情況。
Props 和組合爲你提供了清晰而安全地定製組件外觀和行爲的靈活方式。注意:組件可以接受任意 props,包括基本數據類型,React 元素以及函數。
如果你想要在組件間複用非 UI 的功能,我們建議將其提取爲一個單獨的 JavaScript 模塊,如函數、對象或者類。組件可以直接引入(import)而無需通過 extend 繼承它們。