React 快速上手

React 快速上手

開始

  1. 開始一個React的項目.
npx create-react-app my-app

注意:Node >= 8.10npm >= 5.6,npxnpm 5.2+ 附帶的 package 運行工具

參數說明:

my-app: 指的是項目路徑,比如想在當前目錄創建項目,則npx create-react-app .

過程可能會久一點,安裝完之後,你會發現git已經是創建好的.npm start測試安裝是否成功:
在這裏插入圖片描述如果有以上信息,則成功,默認端口號是3000,按照提示我們訪問,http://localhost:3000,一般會自動訪問.

  1. 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 元素。

定義組件

  1. 開始定義第一個組件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 方法都會被調用.

  1. 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 是私有的,並且完全受控於當前組件。

  1. 先將時間信息放在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;

快速概括一下發生了什麼和這些方法的調用順序:

  1. <Clock /> 被傳給 ReactDOM.render()的時候,React 會調用 Clock 組件的構造函數。因爲 Clock 需要顯示當前的時間,所以它會用一個包含當前時間的對象來初始化 this.state。我們會在之後更新 state。
  2. 之後 React 會調用組件的 render() 方法。這就是 React 確定該在頁面上展示什麼的方式。然後 React 更新 DOM 來匹配 Clock 渲染的輸出。
  3. Clock 的輸出被插入到 DOM 中後,React 就會調用 ComponentDidMount() 生命週期方法。在這個方法中,Clock 組件向瀏覽器請求設置一個計時器來每秒調用setState()
  4. Clock 組件會通過調用 setState() 來計劃進行一次 UI 更新。得益於 setState() 的調用,React 能夠知道 state 已經改變了,然後會重新調用 render() 方法來確定頁面上該顯示什麼。這一次,render() 方法中的 this.state.date 就不一樣了,如此以來就會渲染輸出更新過的時間。React 也會相應的更新 DOM。
  5. 一旦 Clock 組件從 DOM 中被移除,React 就會調用 componentWillUnmount() 生命週期方法,這樣計時器就停止了

關於 setState():

爲什麼不直接修改:this.state.date=new Date()? 代碼沒問題,但是這樣子修改並不會通知組件,state 變了,所以構造函數是唯一可以給this.state賦值的地方.

出於性能考慮,React 可能會把多個 setState() 調用合併成一個調用。

因爲 this.propsthis.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.idprops.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來獲悉是哪個組件,並且namestate裏的屬性名相同.

狀態的提升

現在是有這麼個需求,有兩個輸入框,一個輸入框是攝氏度,一個是華氏度,只要輸入的溫度,兩個框會自動同步轉換,也就是兄弟組件之間怎麼傳遞數據的問題.

先寫溫度輸入框組件:

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 繼承它們。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章