掌握react,這一篇就夠了

2021-03-03 JavaScript

react眾所周知的前端3大主流框架之一,由於出色的性能,完善的周邊設施風頭一時無兩。本文就帶大家一起掌握react。

jsx語法

前端MVVM主流框架都有一套自己的模板處理方法,react則使用它獨特的jsx語法。在組件中插入html類似的語法,簡化創建view的流程。

下面讓我們來認識一下構建的兩種元素

原生元素

ReactDOM.render((

 <div>

   <h1>標題</h1>

 </div>

), document.getElementById('root'))

通過簡單的語法頁面就會被插入一個div+一個h1標籤。原生的html元素可以被直接使用。以上的語法並不是js支持的語法,需要被轉換之後才能運行。

自定義元素

react強大之處就在於可以組件的自定義,實現組件的復用。如果我們創建了一個組件。我們也可以通過jsx語法調用。

import * as React from 'react'

class Page extends React.Component {

 render() {

   return (<div>

     home111 &copy; © \ua9

   </div>)

 }

}

ReactDOM.render((

 <div>

   <Page/>

 </div>

), document.getElementById('root'))

我們定義了一個Page組件,可以在jsx裡面像調用html一樣直接調用。

插入動態數據

let name = 'hi'

ReactDOM.render((

 <div>

   {name}

 </div>

), document.getElementById('root'))

使用{}就可以插入數據,但是{}中間的必須是js表達式,不能是語句。如果表達式的執行結果是一個數組,則會自動join。

注釋

jsx語法和html語法一樣,也是可以插入注釋,只不過寫的時候有一些區別

子組件注釋

let name = 'hi'

ReactDOM.render((

 <div>

   {/* 注釋 */}

   {name}

 </div>

), document.getElementById('root'))

在子組件中插入注釋,需要使用{}包裹起來,在//之間插入注釋文字。

屬性注釋

let name = 'hi'

ReactDOM.render((

 <div>

   {name}

   <img /*

       多行注釋

   */ src="1.jpg"/>

 </div>

), document.getElementById('root'))

在標籤中間,可以插入一個多行注釋,類似上面的代碼。

屬性props

可以向使用html的attr一樣使用屬性,就像下面img的src一樣

let name = 'hi'

ReactDOM.render((

 <div>

   <img src="1.png"/>

 </div>

), document.getElementById('root'))

如果需要傳遞動態屬性,使用{},多個屬性,使用展開運算符

let props = {

   src: '1.png',

   alt: '1圖片'

}

ReactDOM.render((

 <div>

   <img src={"1.png"}/>

   <img {...props}/>

 </div>

), document.getElementById('root'))

兩個轉換,class-->className for-->htmlFor

因為class和for是javascript關鍵字,所以這裡需要用轉換之後名稱

ReactDOM.render((

 <div className="tab">

   <label htmlFor="name">姓名:</label><input id="name"/>

 </div>

), document.getElementById('root'))

布爾屬性

如果一個屬性的值是布爾值,當這個值是true的時候則可以省略=後面的值,只保留key。

ReactDOM.render((

 <div className="tab">

   <input type="text" required/>

   <input type="text" required={true}/>

 </div>

), document.getElementById('root'))

原生元素的自定義屬性

react對元素屬性做了校驗,如果在原生屬性上使用此元素不支持的屬性,則不能編譯成功。必須使用data-前綴

ReactDOM.render((

 <div className="tab">

   <input type="text" data-init="22"/>

 </div>

), document.getElementById('root'))

插入html

如果動態的插入html元素,react出於安全性考慮會自動幫我們轉義。所以一定要動態的插入元素的話,使用dangerouslySetInnerHTML

ReactDOM.render((

 <div className="tab">

   <div dangerouslySetInnerHTML={{__html: '<span>test</span>'}}></div>

 </div>

), document.getElementById('root'))

React組件創建React.createClass

這是舊版本的api,使用React.createClass創建組件,配套的一些api,有getDefaultProps, getinitialstate。官方已經不建議使用了,使用下面新的api替代。

ES6 classes

import * as React from 'react'

class Page extends React.Component {

 render() {

   return (<div>

     home

   </div>)

 }

}

這是一個實現了render方法的class。也是一個基本的react組件。

無狀態函數

function Button(props, context) {

   return (

       <button>

           <em>{props.text}</em>

           <span>{context.name}</span>

       </button>

   );

}

純函數,不存在state,只接受props和state。純函數有優點,優點就是易於測試,無副作用。

React數據流state

state是組件的內部狀態,需要在視圖裡面用到的狀態,才需要放到state裡面去。如下,我們在類上創建一個state屬性,在視圖裡面通過使用this.state.name去引用。而這裡的state定義則代替的是getinitialstate方法。

import * as React from 'react'

class Page extends React.Component {

 state = {

     name: '小明'

 }    

 render() {

   return (<div>

     {this}

   </div>)

 }

}

如何更新state呢,直接更改state其實可以可以的,不過這樣子無法觸發組件視圖的更新機制。所以使用 setState()api。值得注意的是setState是異步的,原因是react內部需要對setState做優化,不是state變了立刻去更新視圖,而是攔截一部分state的改變,等到合適的時機再去更新視圖。

import * as React from 'react'

class Page extends React.Component {

 state = {

     name: '小明'

 }    

 render() {

   setTimeout(() => this.setState({name: '小明兒子'}), 5000)

   return (<div>

     {this.state.name}

   </div>)

 }

}

真實開發中絕不要在render函數裡面去更改state,以上只是為了演示

props

props是組件之間傳遞數據的最主要api, react推崇的是自頂向下的數據流向,也就是組件的數據要從父組件傳給子組件。如果子組件需要向父組件傳遞數據,則需要使用回調函數的方式。

import * as React from 'react'

class Child extends React.Component {

 render() {

   return (<div>

     {this.props.parentName}

   </div>)

 }

}

class Parent extends React.Component {

 state = {

     name: '小明'

 }    

 render() {

   setTimeout(() => this.setState({name: '小明兒子'}), 5000)

   return (<div>

     <Child parentName={this.state.name}/>

   </div>)

 }

}

可以看到Child組件顯示了父組件的name。當父組件狀態更新了,子組件同步更新。那如何在子組件中更改父組件狀態呢?答案是回調函數。

import * as React from 'react'

class Child extends React.Component {

 update() {

     this.props.onChange('小明名字改了')

 }

 render() {

   return (<div>

     {this.props.parentName}

     <button onClick={this.update.bind(this)}>更新</button>

   </div>)

 }

}

class Parent extends React.Component {

 state = {

     name: '小明'

 }    

 changeName(name) {

     this.setState({

         name

     })

 }

 render() {

   setTimeout(() => this.setState({name: '小明兒子'}), 5000)

   return (<div>

     <Child onChange={this.changeName.bind(this)} parentName={this.state.name}/>

   </div>)

 }

}

注意哈:props是不可以更改的,這既不符合react單向數據流思想,也為維護帶來災難。

事件

react裡面的用戶事件都是合成事件,被React封裝過。內部使用的還是事件的委託機制。 常用的事件有點擊事件onClick,input的onChange事件等,官網都可以查到。

合成事件的this指向問題

就像上文一樣,我們綁定事件的方式很奇怪,使用了bind來顯示綁定this的指向。因為傳遞到組件內部的只是一個函數,而脫離了當前對象的函數的this指向是不能指到當前組件的,需要顯示指定。

通過bind

<button onClick={this.update.bind(this)}>更新</button>

構造器內部指定

import * as React from 'react'

class Child extends React.Component {

 constructor(props) {

    super(props)

    this.update = this.update.bind(this)

 }

 update() {

     this.props.onChange('小明名字改了')

 }

 render() {

   return (<div>

     {this.props.parentName}

     <button onClick={this.update}>更新</button>

   </div>)

 }

}

箭頭函數

import * as React from 'react'

class Child extends React.Component {

 update => e = {

     this.props.onChange('小明名字改了')

 }

 render() {

   return (<div>

     {this.props.parentName}

     <button onClick={this.update}>更新</button>

   </div>)

 }

}

裝飾器

import * as React from 'react'

class Child extends React.Component {

 constructor(props) {

    super(props)

 }

 @autoBind

 update() {

     this.props.onChange('小明名字改了')

 }

 render() {

   return (<div>

     {this.props.parentName}

     <button onClick={this.update}>更新</button>

   </div>)

 }

}

裝飾器是es7語法,如果需要使用需要安裝對應的babel:present版本。而typescript則原生支持。

autoBind原理大概就是劫持get方法,get時改變this指向

如何獲得evnt原生事件

通過e.nativeEvent獲取原生事件對象

import * as React from 'react'

class Child extends React.Component {

 constructor(props) {

    super(props)

    this.update = this.update.bind(this)

 }

 update(e) {

     console.log(e.nativeEvent)

 }

 render() {

   return (<div>

     <button onClick={this.update}>更新</button>

   </div>)

 }

}

解決冒泡和取消默認事件

e.preventDefault() //取消默認行為

e.stopPropagation() //取消冒泡

這個和瀏覽器原生事件處理方案是一致的。問題是我們只可以調合成事件的 e的方法,不可以通過 e.nativeEvent方法做這些操作,原因是上文講過的委託。

ReactDomref

特殊的props,ref組件對象的引用,現在官方也不建議直接給ref賦值,需要通過函數來賦值。

ReactDOM.render((

 <div>

   <Calendar ref={ref => this.c = ref} any-ss="text"/>

 </div>

), document.getElementById('root'))

render

頂層api,只有在根組件時候才需要使用。第一個參數是Component,第二個參數是dom節點

findDOMNode

通過傳入component實例獲取此component根dom節點,在這裡可以去dom節點進行操作了,雖然極其不建議這麼做,但是你確實可以做。

unmountComponentAtNode

卸載此組件,並銷毀組件state和事件

接收組件的引用,也就是ref。僅僅是取消掛載,組件還在,如果需要徹底清除的話,需要手動刪掉此dom。

表單onchange配合value

與vue框架不同的是,react如果要實現表單元素變化,狀態同步更新,必須要自己去監聽表單事件。

import * as React from 'react'

class Child extends React.Component {

 state = {

     name: '小明'

 }

 constructor(props) {

    super(props)

    this.update = this.update.bind(this)

 }

 update(e) {

     this.setState({

         name: e.target.value

     })

 }

 render() {

   return (<div>

     <input onChange={this.update} value={this.state.name}/>

   </div>)

 }

}

受控組件和非受控組件

受控組件和非受控組件這些都是指的表單組件,當一個表單的值是通過value改變的而不是通過defaultValue是受控組件,否則就是非受控組件。

下面組件中的input就是受控組件

import * as React from 'react'

class Child extends React.Component {

 state = {

     name: '小明'

 }

 constructor(props) {

    super(props)

    this.update = this.update.bind(this)

 }

 update(e) {

     this.setState({

         name: e.target.value

     })

 }

 render() {

   return (<div>

     <input onChange={this.update} value={this.state.name}/>

   </div>)

 }

}

下面組件中的input是非受控組件

import * as React from 'react'

class Child extends React.Component {

 state = {

     name: '小明'

 }

 constructor(props) {

    super(props)

    this.update = this.update.bind(this)

 }

 update(e) {

     this.setState({

         name: e.target.value

     })

 }

 render() {

   return (<div>

     <input onChange={this.update} defaultValue={this.state.name}/>

   </div>)

 }

}

組件之間通訊父子之間通訊

父子之間通訊又分為父->子,子->父。

因為react單向數據流向的緣故,父->子通信的話直接通過props。父組件數據變動,直接傳遞給子組件。

子->父組件之間就要通過回調函數來通信了,父組件傳遞一個回調函數給子組件,子組件通過調用此函數的方式通知父組件通信。

跨級組件通信

react為了實現祖先組件和後輩組件之間的通信問題,引入了contextApi。

class Button extends React.Component {

 render() {

   return (

     <button style={{background: this.context.color}}>

       {this.props.children}

     </button>

   );

 }

}

Button.contextTypes = {

 color: React.PropTypes.string

};

class Message extends React.Component {

 render() {

   return (

     <div>

       {this.props.text} <Button>Delete</Button>

     </div>

   );

 }

}

class MessageList extends React.Component {

 getChildContext() {

   return {color: "purple"};

 }

 render() {

   const children = this.props.messages.map((message) =>

     <Message text={message.text} />

   );

   return <div>{children}</div>;

 }

}

MessageList.childContextTypes = {

 color: React.PropTypes.string

};

MessageList中的color會自動更新到兒孫組件裡面去,實現跨級啊通信。如果需要反過來通信,則需要藉助其他工具,比如事件系統(Pub/Sub)。

沒有嵌套關係組件之間通信

組件之間通信最主流的兩種方式脫胎於觀察這模式和中介者模式這兩種。

跨級之間通信現在最主流的方式就是觀察這模式的實現Pub/Sub,react社區中的redux也是使用這種方式實現的。

vue2.X版本也去掉了跨組件通信的功能。那如何在2.x中做跨組件通信呢?如果不藉助外力的話,是不是可以使用$parent和$childen的遞歸調用實現全局組件通信呢?比如我想廣播一個事件,我就查找到所有的子組件,挨個觸發$emit(xx),上報一個事件也是同理,只不過需要查找所有的$parent。結合起來就可以實現組件之間的通信,只不過這種查找效率比較低,需要慎用和優化

作者:frontoldman

https://segmentfault.com/a/1190000016281174

相關焦點

  • webpack4+react16+react-router-dom4從零配置到優化,實現路由按需...
    【華為雲·年終盛典】闖關答題 贏華為平板電腦大禮包 夠膽你就來!>>>
  • React Status 中文周刊 #18 - 三種 React 的反面模式
    Lennart Jörgens📘 教程和趣事如何精簡 Styled-Components — 雖然 styled-components 這種把 JavaScript 和 CSS 寫在一起的做法備受推崇,但是本文的作者認為這也會帶來很多重複代碼
  • React Status 中文周刊 #21 - 2020 年 React 大事記回顧
    🔥 2020 年 React 相關點擊量最高的 5 篇文章1.  2020 年 React 應用中常用的工具庫 — 這是一份 2020 年問世的 React 工具庫清單,價值不菲。2.  5 個使用 Hook 編寫組件的常見錯誤 — 他人總結的經驗教訓一直備受青睞,而本文恰恰是這樣一篇文章 - 有關組件創建。
  • 關於react結合redux使用,或許你還應該掌握這些
    若觸到只是盲區想細品,小編建議從上往下看;若覺得自身掌握的可以,可直接跳至文尾,有驚喜!話不多說,安排起來!reactreact概念首先,我們需要知道的是:React是一個聲明式的,高效且靈活的用於構建用戶界面的javascript庫。React可以將一些簡短,獨立的代碼片段(亦稱『組件』)組合成複雜的UI界面。注意:react是一個庫,而不是框架。
  • 【重學React】動手實現一個react-redux
    顯然,我們需要想辦法復用這部分的邏輯,不然會顯得我們很蠢。我們知道,react 中高階組件可以實現邏輯的復用。文中所用到的 Counter 代碼在 https://github.com/YvetteLau/Blog 中的 myreact-redux/counter 中,建議先 clone 代碼,當然啦,如果覺得本文不錯的話,給個star鼓勵。
  • 30 分鐘精通 React 新特性——React Hooks
    這聽起來有點像被詬病的Mixins啊?難道是Mixins要在react中死灰復燃了嗎?當然不會了,等會我們再來談兩者的區別。總而言之,這些hooks的目標就是讓你不再寫class,讓function一統江湖。React為什麼要搞一個Hooks?想要復用一個有狀態的組件太麻煩了!
  • 寫React Hooks前必讀
    爭論下來結論如下:寫還是要寫的;寫hooks前一定要先學習hooks;團隊再出一篇必讀文檔,必須要求每位同學,先讀再寫。寫hooks前的硬性要求;寫hooks常見的幾個注意點。必開規則:{ "plugins": ["react-hooks"], "rules": { "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn" }}其中,react-hooks/exhaustive-deps
  • React + Redux + React-router 全家桶
    web端開發需要搭配React-dom使用import React from 'react';import ReactDOM from 'react-dom';const App = () => (  <div>      <
  • 手寫React-Router源碼,深入理解其原理
    作者:蔣鵬飛原文:https://github.com/dennis-jiang/Front-End-Knowledges/blob/master/Articles/React/React-Router_Code.md上一篇文章我們講了React-Router的基本用法,並實現了常見的前端路由鑑權
  • react腳手架create-react-app入門
    記得關注,每天都在分享不同知識不管是哪個前端框架都支持快速開發,通過腳手架可以讓咱們更便捷的構建項目,無需考慮搭建環境問題,今天來聊聊create-react-app腳手架安裝腳手架>npm install -g create-react-app創建項目create-react-app myapp # myapp是項目的名稱,這樣就會在當前目錄生成一個myapp的項目
  • 關於兒童安全座椅,看這一篇就夠了
    關於兒童安全座椅,看這一篇就夠了關於兒童安全座椅,看這一篇就夠了關於兒童安全座椅,看這一篇就夠了關於兒童安全座椅,看這一篇就夠了關於兒童安全座椅,看這一篇就夠了
  • 我一定把 React-Router 的真諦給你整地明明白白的
    作者:蔣鵬飛原文:https://github.com/dennis-jiang/Front-End-Knowledges/blob/master/Articles/React/React-Router_Code.md上一篇文章我們講了
  • 函數式編程看React Hooks(一)簡單React Hooks實現
    從 react 的變化可以看出,react 走的道路越來越接近於函數式編程,輸入輸出一致性。當然也不是憑空地去往這個方面,而是為了能夠解決更多的問題。以下 三點是 react 官網所提到的 hooks 的動機 https://zh-hans.reactjs.org/docs/hooks-intro.html#motivation代碼重用:在hooks出來之前,常見的代碼重用方式是 HOC 和render props,這兩種方式帶來的問題是:你需要解構自己的組件,同時會帶來很深的組件嵌套複雜的組件邏輯:在class組件中
  • React 16.8 之 React Hook
    React Hook 為何而存在1.在組件之間復用狀態邏輯很難雖然我們可以用render props 和高階組件來解決這一問題,但是這不僅會容易形成「嵌套地獄」,還會面臨一個很尷尬的問題,那就是複雜邏輯難寫
  • 基礎篇章:關於 React Native 之 Switch 和 ProgressBarAndroid 組件的講解
    【回復「1024」,送你一個特別推送】(友情提示:RN學習,從最基礎的開始,大家不要嫌棄太基礎,會的同學請自行略過,希望不要耽誤已經會的同學的寶貴時間)今天我們來講兩個比較簡單的組件的使用方法,分別是 Switch 和 ProgressBarAndroid 組件,由於非常簡單,所以這兩個控制項的講解就直接用一篇文章就夠了
  • 30 分鐘精通 React 今年最勁爆的新特性 —— React Hooks
    這聽起來有點像被詬病的Mixins啊?難道是Mixins要在react中死灰復燃了嗎?當然不會了,等會我們再來談兩者的區別。總而言之,這些hooks的目標就是讓你不再寫class,讓function一統江湖。React為什麼要搞一個Hooks?想要復用一個有狀態的組件太麻煩了!
  • React Status 中文周刊 #26 - Aleph:基於 Deno 的 React 框架
    新年將近,提前預祝大家新年快樂~電腦閱讀,請訪問 https://docschina.org/weekly/react🔥 本周熱門基於 AWS Lambda 的 React 應用服務端渲染 — 通過使用 Lambda 或 Lambda@Edge
  • 寫React Hooks前需要注意什麼?
    爭論下來結論如下:團隊再出一篇必讀文檔,必須要求每位同學,先讀再寫。因此便有了此文。本文主要講兩大點:硬性要求1.    "react-hooks/rules-of-hooks": "error",    "react-hooks/exhaustive-deps": "warn"  }}其中, react-hooks/exhaustive-deps 至少warn,也可以是error。
  • 小提琴學習,有這一篇就夠了
    一小提琴學習的【三大標準】耳音好小提琴沒有固定音,所有的音準都要自己找,因此對耳朵的準確聽辨要求很高,在指法、轉調等都比鋼琴這類鍵盤樂器難很多小提琴被稱為樂器之母,也是現代管弦樂組中最重要的樂器,聲音優雅至極,時而像清脆的鳥鳴,時而像奔流的泉水,掌握小提琴絕對是令人驕傲的事情。歡迎對小提琴感興趣的幼兒家長和小編進行討論,給孩子進行最專業科學的小提琴培訓。
  • 3-6-react-redux
    ,我們要麼是只使用了react或者單獨使用了redux,接下來呢,讓我們來繼續重構之前的計數器的例子,不過這一回呢,我們要正式地協同使用react和redux了:const counter = (state = 0, action) => {  switch (action.type) {    case 'INCREMENT':      return