在開始 React 之前,JavaScript 這些知識點你學好了嗎?

React 完全是關於 JavaScript 的。而且,有很多材料是關於 JavaScript 而不是 React。其中大部分是 JavaScript ES6 及其特性和語法,還包括三元運算符、簡化的語法、this 對象、JavaScript 內置函數(map、reduce、filter)或更一般的概念,如可組合性、可重用性、不變性或高階函數。在剛開始接觸 React 之前,你可能不需要掌握這些基礎知識,但在學習或實踐過程中肯定需要用到它們。

在開始 React 之前先學習 JavaScript

當你開始進入 React 的世界,create-react-app 通常會是你的第一個 React 項目。在搭建好項目後,你將看到下面的這個 React 類組件:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

React 類組件可能不是最好的入手點。新手有許多東西需要消化,不一定與 React 有關:類語句、類方法和繼承。導入語句也會在學習 React 時增加額外的複雜性。儘管主要焦點應該放在 JSX(React 的語法)上,但其他的東西也需要解釋一番。這篇文章主要針對 JavaScript,所以請不用太擔心 React 相關的內容。

如果你也是一個前端黨,無論是在學習前端開發,還是已經工作的,這裏推薦一下我們的前端學習交流q-u-n-:731771211 ,這裏是把夢想照亮的地方,同爲了生活而拼搏奮鬥,大家互相幫助。新手加入即可獲得經過整理的最前沿的前端技術資料,不定時更新技術,與企業需求同步。好友都在裏面交流,每天都會有大牛定時講解前端技術!知識改變命運

點擊:加入

React 和 JavaScript 類

關於 React 類組件,需要用到有關 JavaScript 類的先驗知識。JavaScript 類的概念相對較新。之前,只有 JavaScript 的原型鏈可用於實現繼承。JavaScript 類以原型繼承爲基礎,讓繼承體系變得更簡單。

定義 React 組件的一種方法是使用 JavaScript 類。

class Developer {
  constructor(firstname, lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
  }

  getName() {
    return this.firstname + ' ' + this.lastname;
  }
}

var me = new Developer('Robin', 'Wieruch');

console.log(me.getName());

一個類描述了一個實體,用於創建實體的實例。在使用 new 語句創建類的實例時,會調用這個類的構造函數。類的屬性通常位於構造函數中。此外,類方法(例如 getName())用於讀取(或寫入)實例的數據。類的實例在類中使用 this 對象來表示,但在外部,僅指定給 JavaScript 變量。

在面向對象編程中,類通常用來實現繼承。在 JavaScript 中也一樣,extends 語句可用於讓一個類繼承另一個類。一個子類通過 extends 語句繼承了一個父類的所有功能,還可以添加自己的功能。

class Developer {
  constructor(firstname, lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
  }

  getName() {
    return this.firstname + ' ' + this.lastname;
  }
}

class ReactDeveloper extends Developer {
  getJob() {
    return 'React Developer';
  }
}

var me = new ReactDeveloper('Robin', 'Wieruch');

console.log(me.getName());
console.log(me.getJob());

基本上,要理解 React 的類組件,知道這些就夠了。JavaScript 類用於定義 React 組件,React 組件繼承了從 React 包導入的 React Component 類的所有功能。

import React, { Component } from 'react';

class App extends Component {
  render() {
    return (
      <div>
        <h1>Welcome to React</h1>
      </div>
    );
  }
}

這就是爲什麼 render() 方法在 React 類組件中是必需的:從 React 包導入的 React Component 用它在瀏覽器中顯示某些內容。此外,如果不從 React Component 繼承,將無法使用其他生命週期方法(包括 render() 方法)。例如,如果不繼承,那麼 componentDidMount() 生命週期方法就不存在,因爲現在這個類只是一個普通 JavaScript 類的實例。除了生命週期方法不可用,React 的 API 方法(例如用於本地狀態管理的 this.setState())也不可用。

我們可以通過 JavaScript 類來擴展通用類的行爲。因此,我們可以引入自己的類方法或屬性。

import React, { Component } from 'react';

class App extends Component {
  getGreeting() {
    return 'Welcome to React';
  }

  render() {
    return (
      <div>
        <h1>{this.getGreeting()}</h1>
      </div>
    );
  }
}

現在你應該知道爲什麼 React 使用 JavaScript 類來定義 React 類組件。當你需要訪問 React 的 API(生命週期方法、this.state 和 this.setState())時,可以使用它們。接下來,你將看到如何以不同的方式定義 React 組件,比如不使用 JavaScript 類,因爲有時候你可能不需要使用類方法、生命週期方法或狀態。

儘管我們可以在 React 中使用 JavaScript 類繼承,但這對於 React 來說不是一個理想的結果,因爲 React 更傾向於使用組合而不是繼承。因此,你的 React 組件需要擴展的唯一類應該是 React Component。

React 中的箭頭函數

在給別人培訓 React 時,我在一開始就向他們解釋 JavaScript 的箭頭函數。箭頭函數是 ES6 新增的語言特性之一,讓 JavaScript 向函數式編程更近了一步。

// JavaScript ES5 function
function getGreeting() {
  return 'Welcome to JavaScript';
}

// JavaScript ES6 arrow function with body
const getGreeting = () => {
  return 'Welcome to JavaScript';
}

// JavaScript ES6 arrow function without body and implicit return
const getGreeting = () =>
  'Welcome to JavaScript';

在 React 應用程序中使用 JavaScript 箭頭函數通常是爲了讓代碼保持簡潔和可讀。我很喜歡箭頭函數,總是嘗試將我的函數從 JavaScript ES5 重構成 ES6。在某些時候,當 JavaScript ES5 函數和 JavaScript ES6 函數之間的差異很明顯時,我會使用 JavaScript ES6 的箭頭函數。不過,對 React 新手來說,太多不同的語法可能會讓人不知所措。因此,在 React 中使用它們之前,我會嘗試解釋 JavaScript 函數的不同特點。在以下部分,你將瞭解到如何在 React 中使用 JavaScript 箭頭函數。

在 React 中將函數視爲組件

React 使用了不同的編程範式,這要歸功於 JavaScript 是一門“多面手”編程語言。在面向對象編程方面,React 的類組件可以很好地利用 JavaScript 類(React 組件 API 的繼承、類方法和類屬性,如 this.state)。另一方面,React(及其生態系統)也使用了很多函數式編程的概念。例如,React 的函數無狀態組件是另一種定義 React 組件的方式。那麼,如果可以像函數那樣使用組件,將會怎樣?

function (props) {
  return view;
}

這是一個接收輸入(例如 props)並返回 HTML 元素(視圖)的函數。它不需要管理任何狀態(無狀態),也不需要了解任何方法(類方法、生命週期方法)。這個函數只需要使用 React 組件的 render() 方法來進行渲染。

function Greeting(props) {
  return <h1>{props.greeting}</h1>;
}

功能無狀態組件是在 React 中定義組件的首選方法。它們的樣板代碼較少,複雜性較低,並且比 React 類組件更易於維護。不過,這兩者都有自己存在的理由。

之前提到了 JavaScript 箭頭函數以及它們可以提升代碼的可讀性,現在讓我們將這些函數應用無狀態組件中。之前的 Greeting 組件在 JavaScript ES5 和 ES6 中的寫法有點不一樣:

// JavaScript ES5 function
function Greeting(props) {
  return <h1>{props.greeting}</h1>;
}

// JavaScript ES6 arrow function
const Greeting = (props) => {
  return <h1>{props.greeting}</h1>;
}

// JavaScript ES6 arrow function without body and implicit return
const Greeting = (props) =>
  <h1>{props.greeting}</h1>

JavaScript 箭頭函數是讓 React 無狀態組件保持簡潔的一個不錯的方法。

React 類組件語法

React 定義組件的方式一直在演化。在早期階段,React.createClass() 方法是創建 React 類組件的默認方式。現在不再使用這個方法,因爲隨着 JavaScript ES6 的興起,之前的 React 類組件語法成爲默認語法。

不過,JavaScript 也在不斷髮展,因此 JavaScript 愛好者一直在尋找新的方式。這就是爲什麼你會發現 React 類組件使用了不同的語法。使用狀態和類方法來定義 React 類組件的一種方法如下:

class Counter extends Component {
  constructor(props) {
    super(props);

    this.state = {
      counter: 0,
    };

    this.onIncrement = this.onIncrement.bind(this);
    this.onDecrement = this.onDecrement.bind(this);
  }

  onIncrement() {
    this.setState(state => ({ counter: state.counter + 1 }));
  }

  onDecrement() {
    this.setState(state => ({ counter: state.counter - 1 }));
  }

  render() {
    return (
      <div>
        <p>{this.state.counter}</p>

        <button onClick={this.onIncrement} type="button">Increment</button>
        <button onClick={this.onDecrement} type="button">Decrement</button>
      </div>
    );
  }
}

不過,在實現大量的 React 類組件時,構造函數中的類方法綁定和構造函數本身就變成了繁瑣的實現細節。所運的是,有一個簡短的語法可用來擺脫這兩個煩惱:

class Counter extends Component {
  state = {
    counter: 0,
  };

  onIncrement = () => {
    this.setState(state => ({ counter: state.counter + 1 }));
  }

  onDecrement = () => {
    this.setState(state => ({ counter: state.counter - 1 }));
  }

  render() {
    return (
      <div>
        <p>{this.state.counter}</p>

        <button onClick={this.onIncrement} type="button">Increment</button>
        <button onClick={this.onDecrement} type="button">Decrement</button>
      </div>
    );
  }
}

通過使用 JavaScript 箭頭函數,可以自動綁定類方法,不需要在構造函數中綁定它們。通過將狀態直接定義爲類屬性,在不使用 props 時就可以省略構造函數。(注意:請注意,JavaScript 還不支持類屬性。)因此,你可以說這種定義 React 類組件的方式比其他版本更簡潔。

React 中的模板字面量

模板字面量是 JavaScript ES6 附帶的另一種 JavaScript 語言特性。之所以提到這個特性,是因爲當 JavaScript 和 React 新手看到它們時,可能會感到困惑。以下面的連接字符串的語法爲例:

function getGreeting(what) {
  return 'Welcome to ' + what;
}

const greeting = getGreeting('JavaScript');
console.log(greeting);
// Welcome to JavaScript

模板字面量可以用於達到相同的目的,被稱爲字符串插值:

function getGreeting(what) {
  return `Welcome to ${what}`;
}

你只需使用反引號和 ${}來插入 JavaScript 原語。字符串字面量不僅可用於字符串插值,還可用於多行字符串:

function getGreeting(what) {
  return `
    Welcome
    to
    ${what}
  `;
}

這樣就可以格式化多行文本塊。

React 中 Map、Reduce 和 Filter

在向 React 新手教授 JSX 語法時,我通常會先在 render() 方法中定義一個變量,然後將其用在返回代碼塊中。

import React, { Component } from 'react';

class App extends Component {
  render() {
    var greeting = 'Welcome to React';
    return (
      <div>
        <h1>{greeting}</h1>
      </div>
    );
  }
}

export default App;

你只需使用花括號來操作 HTML 中的 JavaScript。不管是渲染字符串還是渲染一個複雜的對象,並沒有太大不同。

import React, { Component } from 'react';

class App extends Component {
  render() {
    var user = { name: 'Robin' };
    return (
      <div>
        <h1>{user.name}</h1>
      </div>
    );
  }
}

接下來的問題是:如何渲染項目列表?React 沒有提供特定的 API(例如 HTML 標記的自定義屬性)用於渲染項目列表。我們可以使用純 JavaScript 代碼來迭代項目列表,並返回每個項目的 HTML。

import React, { Component } from 'react';

class App extends Component {
  render() {
    var users = [
      { name: 'Robin' },
      { name: 'Markus' },
    ];

    return (
      <ul>
        {users.map(function (user) {
          return <li>{user.name}</li>;
        })}
      </ul>
    );
  }
}

通過使用 JavaScript 箭頭函數,你可以擺脫箭頭函數體和 return 語句,讓渲染輸出更加簡潔。

import React, { Component } from 'react';

class App extends Component {
  render() {
    var users = [
      { name: 'Robin' },
      { name: 'Markus' },
    ];

    return (
      <ul>
        {users.map(user => <li>{user.name}</li>)}
      </ul>
    );
  }
}

很快,每個 React 開發人員都習慣了 JavaScript 內置的 map() 方法。對數組進行 map 並返回每個項的渲染輸出,這樣做非常有用。在某系情況下,結合使用 filter() 或 reduce() 會更有用,而不只是爲每個被 map 的項渲染輸出。

import React, { Component } from 'react';

class App extends Component {
  render() {
    var users = [
      { name: 'Robin', isDeveloper: true },
      { name: 'Markus', isDeveloper: false },
    ];

    return (
      <ul>
        {users
          .filter(user => user.isDeveloper)
          .map(user => <li>{user.name}</li>)
        }
      </ul>
    );
  }
}

通常,React 開發人員習慣於使用 JavaScript 的這些內置函數,而不必使用 React 特定的 API。它只是 HTML 中的 JavaScript。

React 中的 var、let 和 const

對於 React 的新手來說,使用 var、let 和 const 來聲明變量可能也會給他們造成混淆,雖然它們不是 React 相關的。我會嘗試在教學中儘早介紹 let 和 const,並從在 React 組件中交替使用 const 和 var 開始:

import React, { Component } from 'react';

class App extends Component {
  render() {
    const users = [
      { name: 'Robin' },
      { name: 'Markus' },
    ];

    return (
      <ul>
        {users.map(user => <li>{user.name}</li>)}
      </ul>
    );
  }
}

然後我給出了一些使用這些變量聲明的經驗法則:

(1)不要使用 var,因爲 let 和 const 更具體

(2)默認使用 const,因爲它不能被重新分配或重新聲明

(3)如果要重新賦值變量則使用 let

let 通常用於 for 循環中,const 通常用於保持 JavaScript 變量不變。儘管在使用 const 時可以修改對象和數組的內部屬性,但變量聲明表達了保持變量不變的意圖。

React 中的三元運算符

如果要通過 if-else 語句進行條件渲染該怎麼辦?我們不能直接在 JSX 中使用 if-else 語句,但可以從渲染函數中提前返回。如果不需要顯示內容,返回 null 在 React 中是合法的。

import React, { Component } from 'react';

class App extends Component {
  render() {
    const users = [
      { name: 'Robin' },
      { name: 'Markus' },
    ];

    const showUsers = false;

    if (!showUsers) {
      return null;
    }

    return (
      <ul>
        {users.map(user => <li>{user.name}</li>)}
      </ul>
    );
  }
}

不過,如果要在返回的 JSX 中使用 if-else 語句,可以使用 JavaScript 的三元運算符:

import React, { Component } from 'react';

class App extends Component {
  render() {
    const users = [
      { name: 'Robin' },
      { name: 'Markus' },
    ];

    const showUsers = false;

    return (
      <div>
        {
          showUsers ? (
            <ul>
              {users.map(user => <li>{user.name}</li>)}
            </ul>
          ) : (
            null
          )
        }
      </div>
    );
  }
}

如果你只返回條件渲染的一個方面,可以使用 && 運算符:

import React, { Component } from 'react';

class App extends Component {
  render() {
    const users = [
      { name: 'Robin' },
      { name: 'Markus' },
    ];

    const showUsers = false;

    return (
      <div>
        {
          showUsers && (
            <ul>
              {users.map(user => <li>{user.name}</li>)}
            </ul>
          )
        }
      </div>
    );
  }
}

React 中的導入和導出語句

在 JavaScript 中,我們可以通過 import 和 export 語句來導入和導出在 JavaScript ES6 文件中定義的功能。

在開始你的第一個 React 應用程序之前,這些 import 和 export 語句是另一個需要了解的話題。create-react-app 項目已經在使用 import 語句:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h1 className="App-title">Welcome to React</h1>
        </header>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload.
        </p>
      </div>
    );
  }
}

這對初始項目來說非常棒,因爲它爲你提供了一個全面的體驗,可以導入和導出其他文件。不過,在剛開始接觸 React 時,我會試着避免這些導入。相反,我會專注於 JSX 和 React 組件。在需要將 React 組件或 JavaScript 函數分離到單獨的文件中時,才需要引入導入和導出語句。

那麼這樣使用這些導入和導出語句呢?假設你想要導出一個文件的如下變量:

const firstname = 'Robin';
const lastname = 'Wieruch';

export { firstname, lastname };

然後,你可以通過第一個文件的相對路徑將它們導入到另一個文件中:

import { firstname, lastname } from './file1.js';

console.log(firstname);
// output: Robin

因此,它不一定只是與導入或導出組件或函數有關,它可以是共享可分配給變量的所有東西(我們只談 JS)。你還可以將另一個文件導出的所有變量作爲一個對象導入:

import * as person from './file1.js';

console.log(person.firstname);
// output: Robin

導入可以有別名。當從多個文件導入具有相同導出名稱的功能時,就需要用到別名。

import { firstname as username } from './file1.js';

console.log(username);
// output: Robin

之前所有的例子都是命名的導入和導出。除此之外,還有默認的導入和導出。它可以用於以下一些場景:

  • [ ] 導出和導入單個功能;
  • [ ] 強調一個模塊導出 API 的主要功能;
  • [ ] 作爲導入功能的後備。
const robin = {
  firstname: 'Robin',
  lastname: 'Wieruch',
};

export default robin;

在使用默認導入時可以省略大括號:

import developer from './file1.js';

console.log(developer);
// output: { firstname: 'Robin', lastname: 'Wieruch' }

此外,導入名稱可以與導出的默認名稱不同。你還可以將它與命名的 export 和 import 語句一起使用:

const firstname = 'Robin';
const lastname = 'Wieruch';

const person = {
  firstname,
  lastname,
};

export {
  firstname,
  lastname,
};

export default person;

在另一個文件中導入:

import developer, { firstname, lastname } from './file1.js';

console.log(developer);
// output: { firstname: 'Robin', lastname: 'Wieruch' }
console.log(firstname, lastname);
// output: Robin Wieruch

你還可以節省一些行,直接導出命名的變量:

export const firstname = 'Robin';
export const lastname = 'Wieruch';

這些是 ES6 模塊的主要功能。它們可以幫助你更好地組織代碼,並設計出可重用的模塊 API。

React 中的庫

React 只是應用程序的視圖層。React 提供了一些內部狀態管理,但除此之外,它只是一個爲瀏覽器渲染 HTML 的組件庫。API(例如瀏覽器 API、DOM API)、JavaScript 或外部庫可以爲 React 添加額外的東西。爲 React 應用程序選擇合適的庫並不是件容易的事,但一旦你對不同的庫有了很好的瞭解,就可以選擇最適合你的技術棧的庫。

例如,我們可以使用 React 原生的獲取數據的 API 來獲取數據:

import React, { Component } from 'react';

class App extends Component {
  state = {
    data: null,
  };

  componentDidMount() {
    fetch('https://api.mydomain.com')
      .then(response => response.json())
      .then(data => this.setState({ data }));
  }

  render() {
    ...
  }
}

但你也可以使用另一個庫來獲取數據,Axios 就是這樣的一個流行庫:

import React, { Component } from 'react';
import axios from 'axios';

class App extends Component {
  state = {
    data: null,
  };

  componentDidMount() {
    axios.get('https://api.mydomain.com')
      .then(data => this.setState({ data }));
  }

  render() {
    ...
  }
}

因此,一旦你知道了需要解決什麼問題,React 的生態系統就可以爲你提供大量的解決方案。這可能與 React 本身無關,而是有關了解如何選擇可用於彌補 React 應用程序的各種 JavaScript 庫。

React 中的高階函數

高階函數是函數式編程中的一個非常棒的概念。在 React 中,瞭解這些函數是非常有意義的,因爲在某些時候你需要處理高階組件,如果已經瞭解了高階函數,那麼就可以更好地瞭解這些高階組件。

我們假設可以根據一個輸入字段的值對用戶列表進行過濾。

import React, { Component } from 'react';

class App extends Component {
  state = {
    query: '',
  };

  onChange = event => {
    this.setState({ query: event.target.value });
  }

  render() {
    const users = [
      { name: 'Robin' },
      { name: 'Markus' },
    ];

    return (
      <div>
        <ul>
          {users
            .filter(user => this.state.query === user.name)
            .map(user => <li>{user.name}</li>)
          }
        </ul>

        <input
          type="text"
          onChange={this.onChange}
        />
      </div>
    );
  }
}

我們並不總是希望通過提取函數的方式來實現,因爲這樣會增加不必要的複雜性。但是,通過提取函數,我們可以對其進行單獨的測試。因此,讓我們使用內置的 filter 函數來實現這個例子。

import React, { Component } from 'react';

function doFilter(user) {
  return query === user.name;
}

class App extends Component {
  ...

  render() {
    const users = [
      { name: 'Robin' },
      { name: 'Markus' },
    ];

    return (
      <div>
        <ul>
          {users
            .filter(doFilter)
            .map(user => <li>{user.name}</li>)
          }
        </ul>

        <input
          type="text"
          onChange={this.onChange}
        />
      </div>
    );
  }
}

這個實現還起不到作用,因爲 doFilter() 函數需要知道 state 的 query 屬性。我們可以通過另一個包裝函數來傳遞它,也就是高階函數。

import React, { Component } from 'react';

function doFilter(query) {
  return function (user) {
    return query === user.name;
  }
}

class App extends Component {
  ...

  render() {
    const users = [
      { name: 'Robin' },
      { name: 'Markus' },
    ];

    return (
      <div>
        <ul>
          {users
            .filter(doFilter(this.state.query))
            .map(user => <li>{user.name}</li>)
          }
        </ul>

        <input
          type="text"
          onChange={this.onChange}
        />
      </div>
    );
  }
}

基本上,高階函數是可以返回函數的函數。通過使用 JavaScript ES6 的箭頭函數,你可以讓高階函數變得更簡潔。此外,這種簡化的方式讓將函數組合成函數變得更吸引人。

const doFilter = query => user =>
  query === user.name;

現在可以將 doFilter() 函數從文件中導出,並將其作爲純(高階)函數進行單獨的測試。在瞭解了高階函數之後,就爲學習 React 的高階組件奠定了基礎。

將這些函數提取到 React 組件之外的(高階)函數中也助於單獨測試 React 的本地狀態管理。

export const doIncrement = state =>
  ({ counter: state.counter + 1 });

export const doDecrement = state =>
  ({ counter: state.counter - 1 });

class Counter extends Component {
  state = {
    counter: 0,
  };

  onIncrement = () => {
    this.setState(doIncrement);
  }

  onDecrement = () => {
    this.setState(doDecrement);
  }

  render() {
    return (
      <div>
        <p>{this.state.counter}</p>

        <button onClick={this.onIncrement} type="button">Increment</button>
        <button onClick={this.onDecrement} type="button">Decrement</button>
      </div>
    );
  }
}

函數式編程非常強大,轉向函數式編程有助於瞭解 JavaScript 將函數作爲一等公民所帶來的好處。

React 中的解構和展開運算符

JavaScript 中引入的另一種語言特性稱爲解構。通常情況下,你需要在組件的 state 或 props 中訪問大量的屬性。你可以在 JavaScript 中使用解構賦值,而不是逐個將它們分配給變量。

// no destructuring
const users = this.state.users;
const counter = this.state.counter;

// destructuring
const { users, counter } = this.state;

這對函數式無狀態組件來說特別有用,因爲它們可以在函數簽名中收到 props 對象。通常,你用到的不是 props,而是 props 裏的內容,因此你可以對函數簽名中已有的內容進行解構。

// no destructuring
function Greeting(props) {
  return <h1>{props.greeting}</h1>;
}

// destructuring
function Greeting({ greeting }) {
  return <h1>{greeting}</h1>;
}

解構也適用於 JavaScript 數組。另一個很棒的特性是剩餘解構。它通常用於拆分對象的一部分屬性,並將剩餘屬性保留在另一個對象中。

// rest destructuring
const { users, ...rest } = this.state;

uesrs 可以在 React 組件中渲染,而剩餘狀態可以用在其他地方。這就是 JavaScript 展開(spread)運算發揮作用的地方,它可以將對象的剩餘部分轉到下一個組件。

JavaScript 多過 React

React 只提供了一個細小的 API 表面區域,因此開發人員必須習慣於 JavaScript 提供的所有功能。這句話並非沒有任何理由:“成爲 React 開發者也會讓你成爲更好的 JavaScript 開發者”。讓我們通過重構一個高階組件來回顧一下學到的 JavaScript 的一些方面。

function withLoading(Component) {
  return class WithLoading extends {
    render() {
      const { isLoading, ...props } = this.props;

      if (isLoading) {
        return <p>Loading</p>;
      }

      return <Component { ...props } />;
    }
  }
  };
}

這個高階組件用於顯示條件加載進度條,當 isLoading 被設爲 true 時,就可以顯示加載進度條,否則就渲染輸入組件。在這裏可以看到(剩餘)解構和展開運算符的實際應用。後者可以在渲染的 Component 中看到,因爲 props 對象的剩餘屬性被傳給了那個 Component。

讓高階組件變得更簡潔的第一步是將返回的 React 類組件重構爲函數式無狀態組件:

function withLoading(Component) {
  return function ({ isLoading, ...props }) {
    if (isLoading) {
      return <p>Loading</p>;
    }

    return <Component { ...props } />;
  };
}

可以看到,剩餘解構也可以被用在函數的簽名中。接下來,使用 JavaScript ES6 箭頭函數讓高階組件變得更加簡潔:

const withLoading = Component => ({ isLoading, ...props }) => {
  if (isLoading) {
    return <p>Loading</p>;
  }

  return <Component { ...props } />;
}

通過使用三元運算符可以將函數體縮短爲一行代碼。因此可以省略函數體和 return 語句。

const withLoading = Component => ({ isLoading, ...props }) =>
  isLoading
    ? <p>Loading</p>
    : <Component { ...props } />

如你所見,高階組件使用的是各種 JavaScript 而不是 React 相關技術:箭頭函數、高階函數、三元運算符、解構和展開運算符。

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