目錄
元件即函數
props 和state 的實作
小結
首頁 web前端 js教程 對於 React 元件和state|props的解析

對於 React 元件和state|props的解析

Jul 13, 2018 pm 03:10 PM
react.js

閱讀原始碼一個痛處是會陷進理不順主幹的困局中,本系列文章在實現一個(x)react 的同時理順React 框架的主幹內容(JSX/虛擬DOM/元件/... )

元件即函數

在上一篇JSX 和Virtual DOM 中,解釋了JSX 渲染到介面的過程並實作了對應程式碼,程式碼呼叫如下所示:

import React from 'react'
import ReactDOM from 'react-dom'

const element = (
  <p>
    hello<span>world!</span>
  </p>
)

ReactDOM.render(
  element,
  document.getElementById('root')
)
登入後複製

本小節,我們接著探究元件渲染到介面的過程。在此我們引入元件的概念,元件本質上就是一個函數,如下就是一段標準元件程式碼:

import React from 'react'

// 写法 1:
class A {
  render() {
    return <p>I'm componentA</p>
  }
}

// 写法 2:无状态组件
const A = () => <p>I'm componentA</p>

ReactDOM.render(<a></a>, document.body)
登入後複製

<a name="componentA"></a> 是JSX 的寫法,和上一篇同理,babel 將其轉化為React.createElement() 的形式,轉化結果如下所示:

React.createElement(A, null)
登入後複製

可以看到當JSX 中是自訂元件的時候,createElement 後接的第一個參數變為了函數,在repl 打印<a name="componentA"></a>,結果如下:

{
  attributes: undefined,
  children: [],
  key: undefined,
  nodeName: ƒ A()
}
登入後複製

注意這時返回的Virtual DOM 中的nodeName 也變成函數。根據這些線索,我們將先前的 render 函數進行改造。

function render(vdom, container) {
  if (_.isFunction(vdom.nodeName)) { // 如果 JSX 中是自定义组件
    let component, returnVdom
    if (vdom.nodeName.prototype.render) {
      component = new vdom.nodeName()
      returnVdom = component.render()
    } else {
      returnVdom = vdom.nodeName() // 针对无状态组件:const A = () => <p>I'm componentsA</p>
    }
    render(returnVdom, container)
    return
  }
}
登入後複製

至此,我們完成了元件的處理邏輯。

props 和state 的實作

在上個小節元件A 中,是沒有引入任何屬性和狀態的,我們希望元件間能進行屬性的傳遞(props)以及元件內能進行狀態的記錄(state)。

import React, { Component } from 'react'

class A extends Component {
  render() {
    return <p>I'm {this.props.name}</p>
  }
}

ReactDOM.render(<a></a>, document.body)
登入後複製

在上面這段程式碼中,看到 A 函數繼承自 Component。我們來建構這個父類別 Component,並在其上加入 state、props、setState 等屬性方法,從而讓子類別繼承到它們。

function Component(props) {
  this.props = props
  this.state = this.state || {}
}
登入後複製

首先,我們將元件外的props 傳進元件內,修改render 函數中以下程式碼:

function render(vdom, container) {
  if (_.isFunction(vdom.nodeName)) {
    let component, returnVdom
    if (vdom.nodeName.prototype.render) {
      component = new vdom.nodeName(vdom.attributes) // 将组件外的 props 传进组件内
      returnVdom = component.render()
    } else {
      returnVdom = vdom.nodeName(vdom.attributes)     // 处理无状态组件:const A = (props) => <p>I'm {props.name}</p>
    }
    ...
  }
  ...
}
登入後複製

實現完元件間props 的傳遞後,再來聊聊state,在react中是透過setState 來完成元件狀態的改變的,後續章節會對這個api(非同步)深入探究,這裡簡單實作如下:

function Component(props) {
  this.props = props
  this.state = this.state || {}
}

Component.prototype.setState = function() {
  this.state = Object.assign({}, this.state, updateObj) // 这里简单实现,后续篇章会深入探究
  const returnVdom = this.render() // 重新渲染
  document.getElementById('root').innerHTML = null
  render(returnVdom, document.getElementById('root'))
}
登入後複製

此時雖然已經實作了setState 的功能,但是 document.getElementById('root') 節點寫死在setState 中顯然不是我們希望的,我們將dom 節點相關轉移到_render 函數中:

Component.prototype.setState = function(updateObj) {
  this.state = Object.assign({}, this.state, updateObj)
  _render(this) // 重新渲染
}
登入後複製

自然地,重構與之相關的render 函數:

function render(vdom, container) {
  let component
  if (_.isFunction(vdom.nodeName)) {
    if (vdom.nodeName.prototype.render) {
      component = new vdom.nodeName(vdom.attributes)
    } else {
      component = vdom.nodeName(vdom.attributes) // 处理无状态组件:const A = (props) => <p>I'm {props.name}</p>
    }
  }
  component ? _render(component, container) : _render(vdom, container)
}
登入後複製

在render 函數中分離出_render 函數的目的是為了讓setState 函數中也能呼叫_render 邏輯。完整 _render 函數如下:

function _render(component, container) {
  const vdom = component.render ? component.render() : component
  if (_.isString(vdom) || _.isNumber(vdom)) {
    container.innerText = container.innerText + vdom
    return
  }
  const dom = document.createElement(vdom.nodeName)
  for (let attr in vdom.attributes) {
    setAttribute(dom, attr, vdom.attributes[attr])
  }
  vdom.children.forEach(vdomChild => render(vdomChild, dom))
  if (component.container) {  // 注意:调用 setState 方法时是进入这段逻辑,从而实现我们将 dom 的逻辑与 setState 函数分离的目标;知识点: new 出来的同一个实例
    component.container.innerHTML = null
    component.container.appendChild(dom)
    return
  }
  component.container = container
  container.appendChild(dom)
}
登入後複製

讓我們用下面這個用例跑下寫好的 react 吧!

class A extends Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 1
    }
  }

  click() {
    this.setState({
      count: ++this.state.count
    })
  }

  render() {
    return (
      <p>
        <button>Click Me!</button>
        </p><p>{this.props.name}:{this.state.count}</p>
      
    )
  }
}

ReactDOM.render(
  <a></a>,
  document.getElementById('root')
)
登入後複製

效果圖如下:

對於 React 元件和state|props的解析

至此,我們實作了 props 和 state 部分的邏輯。

小結

元件即函數;當JSX 中是自訂元件時,經過babel 轉換後的React.createElement(fn, ..) 後中的第一個參數變為了函數,除此之外其它邏輯與JSX 中為html 元素的時候相同;

此外我們將state/props/setState 等api 封裝進了父類React.Component 中,從而在子類中能調用這些屬性和方法。

以上就是本文的全部內容,希望對大家的學習有所幫助,更多相關內容請關注PHP中文網!

相關推薦:

vue中config/index.js:配置的詳解

以上是對於 React 元件和state|props的解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

React父元件怎麼呼叫子元件的方法 React父元件怎麼呼叫子元件的方法 Dec 27, 2022 pm 07:01 PM

呼叫方法:1、類別元件中的呼叫可以利用React.createRef()、ref的函數式宣告或props自訂onRef屬性來實作;2、函式元件、Hook元件中的呼叫可以利用useImperativeHandle或forwardRef拋出子組件ref來實作。

怎麼調試R​​eact源碼?多種工具下的除錯方法介紹 怎麼調試R​​eact源碼?多種工具下的除錯方法介紹 Mar 31, 2023 pm 06:54 PM

怎麼調試R​​eact源碼?以下這篇文章帶大家聊聊多種工具下的調試React源碼的方法,介紹一下在貢獻者、create-react-app、vite專案中如何debugger React的真實源碼,希望對大家有所幫助!

深入理解React的自訂Hook 深入理解React的自訂Hook Apr 20, 2023 pm 06:22 PM

React 自訂 Hook 是將元件邏輯封裝在可重複使用函數中的方式,它們提供了一種在不編寫類別的情況下重複使用狀態邏輯的方式。本文將詳細介紹如何自訂封裝 hook。

React為什麼不將Vite作為構建應用的首選 React為什麼不將Vite作為構建應用的首選 Feb 03, 2023 pm 06:41 PM

React為什麼不將Vite作為建置應用的首選?以下這篇文章就來帶大家聊聊React不將Vite當作預設推薦的原因,希望對大家有幫助!

react怎麼設定div高度 react怎麼設定div高度 Jan 06, 2023 am 10:19 AM

react設定div高度的方法:1、透過css方式實現div高度;2、在state中宣告一個物件C,並在該物件中存放更換按鈕的樣式,然後取得A並重新設定C中的「marginTop」即可。

7 個很棒且實用的React 元件庫(壓箱底分享) 7 個很棒且實用的React 元件庫(壓箱底分享) Nov 04, 2022 pm 08:00 PM

這篇文章跟大家整理分享7 個很棒又實用的React 元件庫,日常開發中常會用到的,快來收藏試試看吧!

10 個編寫更簡潔React程式碼的實用小技巧 10 個編寫更簡潔React程式碼的實用小技巧 Jan 03, 2023 pm 08:18 PM

這篇文章為大家整理分享 10 個寫更簡潔 React 程式碼的實用小技巧,希望對大家有幫助!

【翻譯】使用自訂hooks對React元件進行重構 【翻譯】使用自訂hooks對React元件進行重構 Jan 17, 2023 pm 08:13 PM

我時常會聽到人們談起React函數元件,提到函數元件會不可避免的變得體積更大,邏輯更複雜。畢竟,我們把元件寫在了「一個函數」裡,因此你不得不接受元件會膨脹導致這個函數會不斷膨脹。

See all articles