React.js 常見問題

jsComplete,咱們管理一個專門用於幫助編程學習者 slack 賬戶。咱們經常會收到一些有趣的問題,但大多數問題都是常見問題。 我建立這個資源爲了幫助 React.js學習者遇到這些常見的問題時提供必定幫助。在這裏能夠快速找到一些常見問題的解決方案,而不是一,遍又一遍去找解決方法,我會持續更新這些常見的問題。前端

想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等着你!node

1. 組件的名稱開頭要大寫

React 組件名稱必須具備以大寫字母開頭。react

若是組件名稱不以大寫字母開頭,則組件使用將被視爲內置元素,例如 divspangit

例如:github

class greeting extends React.Component { 
  // ...
}

若是嘗試渲染 <greeting />,React 將忽略上述內容,會報如下警告:web

Warning: The tag <greeting> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.

這裏更大的問題是把組件命名爲 buttonimg。 React 會忽略你的組件,只是渲染一個普通 HTML buttonimg 標籤。算法

圖片描述

注意上面沒有渲染 「My Awesome Button」 和 React 剛剛呈現的空 HTML 按鈕元素。 在這種狀況下,React 不會警告你。mongodb

2.使用單引號而不是反引號

用反引號(`…`)建立的字符串與用單引號('…')建立的字符串不一樣。shell

在大多數鍵盤上,可使用 tab 鍵上方的鍵來輸入用反引號( ` )字符。數據庫

當須要在字符串中包含動態表達式時,使用反引號建立一個字符串(不須要使用字符串鏈接)。

`This is a string template literal that can include expressions`
'This is just a string, you cannot include expressions here'

假設你想要一個始終報告當前時間的字符串:

「Time is ...」

// Current time string
const time = new Date().toLocaleTimeString();
// 使用普通字符串(單引號或雙引號)時,須要使用字符串鏈接:
'Time is ' + time
// 在使用反引號時,可使用 ${} 在字符串中注入時間
`Time is ${time}`

此外,反引號還聲明一個字符串時,能夠建立一個跨多行的字符串:

const template = `I
CAN
SPAN
Multiple Lines`;

常規字符串不能這樣作。

3. 使用 React.PropTypes

PropTypes 對象已從 React 中刪除。 其過去是以 React.PropTypes 的形式被使用,但不能再使用它了。

相應的,你須要:

  1. npm install prop-types
  2. import PropTypes from 'prop-types'

而後就可使用它啦,如: PropTypes.string

若是你錯誤地使用了 React.PropTypes,會獲得這樣的錯誤提示:

TypeError: Cannot read property 'string' of undefined

4. 沒有使用指南里指定的版本

在看或閱讀有關代碼的內容以及使用指南里的例子時,確保你使用的庫版本與例子的裏的版本是一致的。通常使用最新版本是沒有問題,可是若是內容過期,你可能會遇到一些棄用問題。

爲了安全起見,請使用主幹版本。 例如,若是教程裏使用的是 React 16,本身就不要使用 React 15 進行開發了。

這對 Node.js 也很重要。若是使用舊版本的 Node,會遇到一系列問題。 例如,若是你正在看一些教程,這些教程使用了 Object.values,而你如今用 Node 6.x,那個版本此方法是不存在的。 你須要 Node 7.x 或更高版本。

5. 使人困惑的函數與類

你能看出下面的代碼有什麼問題嗎?

class Numbers extends React.Component {
  const arrayOfNumbers = _.range(1, 10);
  // ...
}

上面的代碼是無效的,由於在 JavaScript 類的內部,不能隨意定義變量,只能使用規定的語法定義方法和屬性。

這有點使人困惑,由於類語法中使用的{}看起來像塊級做用域,但它並非。

在一個由函數構成的組件裏,你就能夠想怎麼搞就怎麼搞:

// Totally Okay:
const Number = (props) => {
  const arrayOfNumbers = _.range(1, 10);
  // ...
};

6. 將數字做爲字符串傳遞

你能夠經過 prop 屬性傳遞一個字符串:

<Greeting name="World" />

若是須要傳遞一個數值,不要使用字符串:

// 不要這樣作
<Greeting counter="7" />

相反,使用花括號傳遞一個實際的數值:

<Greeting counter={7} />

在 Greeting 組件中使用 {7}this.props.counter 就被賦值爲數字 7 ,而且能夠對其進行數學運算。 若是將其做爲「7」傳遞,而後將其視爲數字,則可能會遇到意外結果。

7. 忘記了另一個 app 在用一樣的端口

要運行 web 服務器,須要使用主機(如 127.0.0.1 )和端口(如 8080)使服務器偵聽有效 http 地址上的請求。

一旦成功運行,web 服務器就佔據了那個端口,你就不能讓這個端口它用,端口會被佔用。

若是你嘗試在另外一個終端上運行相同的服務器,將會獲得端口被佔用的錯誤提示,以下:

Error: listen EADDRINUSE 127.0.0.1:8080

請注意,有時 Web 服務器可能在後臺運行或在分離的屏幕/tmux 會話中運行。你看不到,但它仍然佔據了端口。 要從新啓動服務器,須要「殺死」仍在運行的服務器。

要識別使用某個端口的進程,可使用 ps 之類的命令(以及關於應用程序的grep),或者若是你知道端口號,則可使用 lsof 命令:

lsof -i :8080

8. 忘記建立環境變量

有些項目依賴於 shell 環境變量的存在才能啓動。 若是在沒有所需環境變量的狀況下運行這些項目,它們將嘗試爲它們使用未定義的值,並可能會給你一些神祕的錯誤。

例如,若是項目鏈接到像 MongoDB 這樣的數據庫,則可能會使用像 process.env.MONGO_URI這樣的環境變量來鏈接它。 這容許項目與不一樣環境中的不一樣MongoDB 實例一塊兒使用。

要在本地運行鏈接到 MongoDB 的項目,首先必須導出 MONGO_URI 環境變量。 例如,若是你在端口 27017 上運行本地 MongoDB,則須要在運行項目以前執行此操做:

export MONGO_URI="mongodb://localhost:27017/mydb"

你能夠 grep 項目源代碼,找到 process.env 來查清楚其運行正常所須要的環境變量。

9. 把花括號{}和圓括號()搞混

不要用:

return {
  something();
};

這樣用:

return (
  something();
);

第一個將嘗試(而且會失敗)返回一個對象,而第二個將正確調用 something() 函數並返回該函數返回的內容。

由於 JSX 中的任何 <tag> 都將轉換爲函數調用,因此在返回任何 JSX 時都會出現這個問題。

這個問題在箭頭函數的縮寫語法中也很常見。

不要用:

const Greeting = () => {
  <div>
    Hello World
  </div>
};

這樣用:

const Greeting = () => (
  <div>
    Hello World
  </div>
);

當你使用帶有箭頭函數的中括號時,你就新起了一個函數的做用域。箭頭函數的縮寫語法不用中括號。

10. 不使用圓括號包裝對象

當你想要建立一個返回普通對象的箭頭函數時,上面的花括號和圓括號問題也會讓你感到困惑。

不要用:

const myAction = () => { type: 'DO_THIS' };

這樣用:

const myAction = () => ({ type: 'DO_THIS'});

若是沒有把對象包裹在圓括號裏,你就不能使用縮寫語法。實際上你會給字符串定義一個標籤。

這在 setState 方法的 updater 函數中很常見,由於它須要返回一個對象。 若是要使用箭頭函數語法,則須要用括號包裝該對象。

不要用:

this.setState(prevState => { answer: 42 });

這樣用:

this.setState(prevState => ({ answer: 42 }));

11. 沒有正確使用API元素和屬性的大小寫

使用 React.Component,而不是 React.component。 使用 componentDidMount,而不是 ComponentDidMount。使用 ReactDOM,而不是ReactDom

請注意須要的 API 大小寫。 若是使用不正確的大小寫,獲得的錯誤信息可能不會很明確。

reactreact-dom 導入時,確保引入正確的名稱,而且使用的內容與引入徹底相同,ESLint 能夠幫助你指出未使用的內容。

在處理組件屬性時也會遇到這種問題:

<Greeting userName="Max" />
// 在組件內部,你須要使用 props.userName 來獲取傳入的值

若是你沒有使用 props.userName,而是 props.usernameprops.UserName,你會至關於用了一個 undefined 的值。須要特別留意下這點,固然更好的是配置 ESLint,它也能指出這些問題。

12. 將 state 對象與實例屬性搞混

在類組件中,能夠定義本地 state 對象,而後使用 this 訪問它:

class Greeting extends React.Component {
  state = {
    name: "World",
  };
  render() {
    return `Hello ${this.state.name}`;
  }
}

以上代碼會輸出 「Hello World」

state 以外,你還能夠定義其它本地實例屬性。

class Greeting extends React.Component {
  user = {
    name: "World",
  };
  render() {
    return `Hello ${this.user.name}`;
  }
}

以上代碼也會輸出 「Hello World」

state 實例屬性是一個特殊屬性,由於 React 會管理它。 你只能經過 setState 更改它,當你這樣作時 React 會作出響應。

然而,定義的全部其餘實例屬性對渲染算法沒有影響。 你能夠根據須要在上面的示例中更改this.user,而且 React 不會在React中觸發渲染機制。

13. 將 <tag />與</ tag> 搞混

在閉合標籤裏放錯 / 字符。不能否認,有時你可使用 <tag/>,而其餘時間你須要</tag>

在 HTML 中,有一種稱爲「自閉合標籤」(AKA void tag)。這些是表示沒有任何子節點的元素的標記。例如,img 標籤是一個自閉合標籤:

<img src="..." />
// 沒必要使用 <img></img>

div標籤能夠包含子項,所以你可使用開始和結束標記:

<div>
  Children here...
</div>

這一樣適用於 Reac t組件,若是組件具備子元素,以下所示:

<Greeting>Hello!</Greeting>
// 注意/字符的位置

若是組件沒有子元素,能夠用開始/結束標籤書寫,或者用自閉合標籤:

// 兩種方式
<Greeting></Greeting>
<Greeting />

如下方式是非法的:

// 錯誤
<Greeting><Greeting />

若是放錯放了 / 字符的位置,將收到以下錯誤:

Syntax error: Unterminated JSX contents

14. 假設 import/export 起做用

import/export 特性是 JavaScript 中的官方功能(自2015年起)。 其只是 ES2015 的特性,而且尚未在流行瀏覽器和最新版 Node 裏面被完整支持。

React 項目的流行配置使用 Webpack 和 Babel。 二者都容許使用此特性並將其編譯爲全部瀏覽器都能理解的內容。 只有工做流中有 Webpack 或 Babel 之類的轉譯工具,才能使用 import/export

可是,在 React 打包應用程序中 import/export 不意味着能夠隨意使用它們! 例如,若是你還經過最新的 Node 進行服務器端渲染,會行不通,你極可能會收到「unexpected token」 錯誤。

要讓 Node 理解 import/export(若是你在前端使用它們就是你須要瞭解它們,而且也想要作 SSR 渲染的話),你須要有能夠編譯其的 Babel preset(像是_env_ preset),才能在Node 端運行。 你能夠在開發時使用像 pm2_、 _nodemon 和 _babel-watch_的工具作到這點,,並在每次更改內容時從新啓動 Node。

15. 不綁定處理程序方法

我把這個留到最後,由於這是一個大問題,一個很常見的問題。

你能夠在 React 組件中定義類方法,而後在組件的 render 方法中使用它們。 例如:

class Greeting extends React.Component {
  whoIsThis() {
    console.dir(this); // "this" is the caller of whoIsThis
    return "World";
  }
  render() {
    return `Hello ${this.whoIsThis()}`;
  }
}
ReactDOM.render(<Greeting />, mountNode);

我在 render 裏以 this.whoIsThis 的方式調用 whoIsThis 方法,由於在 render中,this 關鍵字指的是與表示組件的 DOM 元素關聯的組件實例。

內部 React 確保其類方法中的 「this」 指向實例。 可是,當你使用 whoIsThis 方法的引用時,JavaScript 不會自動綁定實例。

whoIsThis 方法中的 console.dir 能夠正確告訴咱們當前組件實例,由於該方法是使用顯式調用方(this)直接從 render 方法調用的。 執行上面的代碼時,在控制檯中看到Greeting 對象:

圖片描述

然而,當在延遲執行通道,例如事件處理裏執行一樣方法時,調用對象將再也不是顯性的,console.dir 也不會打印當前組件實例。

圖片描述

在上面的代碼中,當單擊字符串時,React 會調用 whoIsThis 方法,但它不會讓你訪問組件實例。 這就是咱們點擊字符串時獲得 undefined 的緣由。 若是類方法須要訪問像this.propsthis.state 這樣的屬性,這會是一個問題,由於它根本行不通。

對於這個問題有不少解決方案。能夠將方法包裝在內聯函數中,或使用 .bind 來改變 this 指向。 對於不常常更新的組件,二者均可以。

還能夠經過在類的構造函數中而不是在 render 方法中執行來優化 bind 方法。 可是,此方法的最佳解決方案是經過 Babel 來使用 ECMAScript 類字段我(目前仍是stage-3),這樣對於處理程序只需使用箭頭函數就能夠了:

class Greeting extends React.Component {
  whoIsThis = () => {
    console.dir(this);
  }
  render() {
    return (
     <div onClick={this.whoIsThis}>
        Hello World
      </div>
    );
  }
}

這樣會和預期同樣執行:

圖片描述

代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug

原文:

https://edgecoders.com/react-...

你的點贊是我持續分享好東西的動力,歡迎點贊!

歡迎加入前端你們庭,裏面會常常分享一些技術資源。

clipboard.png

相關文章
相關標籤/搜索