React反模式 —— 如何不使用JSX地動態顯示組件


歡迎指導與討論 : )

  前言

    文章的最后能寫出以 Modal.open( ) 這種調用形式,動態顯示React對話框組件的寫法(類似於ant design),同時涉及數據交互(數據能異步地返回給調用者)。筆者將和大家一起探討這種寫法的大概思路、難點,細節問題歡迎指出和補充。 O(∩_∩)O

    本文Demo地址:https://github.com/Penggggg/react-model-demo

    關於Angular-1的動態組件的思路的Demo地址:https://github.com/Penggggg/angular-component-practices

    歡迎 Star ~

 

  最終效果圖

// 調用代碼
import Model from './Model;

    onClick = ( ) => {
        Model.show({ msg: 'Are you a Man?', title: 'Hello!' })
            .then(( value: string ) => {
               // 點擊Yes按鈕后返回的數據
                console.log(` value: ${value}`);
            })
            .catch( e => {
              // 點擊No按鈕后返回的數據
                 console.log(`e`);
            })
    }    

 

  探討1:如何不使用JSX也能顯示React.Component實例

    主要思路是:實例化一個組件 --> 渲染成Dom --> 插入到body元素內。

    其中實例化與渲染成Dom可以使用ReactDomrender函數去完成:  let _model = ReactDom.render(<Model />, div); 其中 div 是這個組件實例的一個容器(container dom),我們可以這樣生產這個容器dom:  let div = document.createElement('div'); 。最后只需要把渲染結果插入到body元素中: document.querySelector('body').appendChild( div );

// 組合起來就是
 let div = document.createElement('div'); let _model = ReactDom.render(<Model  />, div);
let dom = document.querySelector('body').appendChild(div);

 

  探討2:交互數據如何異步地返回給調用者

    由於Modal組件涉及到數據交互(我們需要知道用戶點擊了Yes還是No),因此我們還需要將完成點擊之后的數據異步地返回給調用者。同時,由於Modal的調用模式是 Modal.show( ) ,因此我決定在 Modal.show( ) 執行時,返回一個Promise給調用者

// 因此,整個調用模式看起來會像:
 Model.show( ) .then(( value: string ) => { // 點擊Yes后的代碼(可以拿到數據value)
 }) .catch( e => { // 點擊No后的代碼
     })

    現在就剩下如何實現上面所說的了。

    首先由於Modal是一個Class(類),因此我們需要把show函數作為Model 類的靜態方法:

// Modal.js
 export default class Model extends React.PureComponent{ static show( ) { // .... }
 }

    其次,我們需要返回一個實例的Promise,並且能夠在Modal類方法地任意地方能夠調用這個Promise實例  resolve( ) 和  reject( ) 方法從返回數據給調用者,因此我們把代碼改動為

// Modal.js

export default class Model extends React.PureComponent{
     _resolve;
     _reject;
     _promise = new Promise (( resolve, reject) => {
        this._resolve = resolve;
        this._reject = reject;
    })

     static show( ) { // .... }
  
}

    然后,為靜態方法show( )函數加上一些邏輯:

// 靜態方法show( )

 static show( ) {
        // 彈出成功!
        let div = document.createElement('div');
        let _model: any = ReactDom.render(<Model />, div);
        let dom = document.querySelector('body').appendChild(div);

       // 還記得我們需要return 一個promise嗎
       return _model._promise;
} 

    注意,這里的重點是:ReactDom.render( )會返回一個組件實例的js引用,而body.append( )會返回這個組件實例的dom引用,這兩點很重要。

 

  探討3:點擊按鈕執行返回數據

    有了上面的代碼,這部分的實現就容易多了。首先先在Model的render函數里面把對話框的基本結構寫出來(JSX),然后是為Yes/No按鈕<button >分別添加一個react的點擊事件

// Modal render函數
 render( ) { let { title, msg } = this.props; return( <div className="m_bg">
                <div className="m_body">
                    <h3 className="title">{title}</h3>
                    <div className="msg">{ msg }</div>
                    <div className="box">
                        <a onClick={this.onNo}>No</a>
                        <a onClick={this.onYes}>Yes</a>
                    </div>
                </div>
            </div>
 ) }
// Yes/No按鈕點擊后的處理函數
    
 onNo = ( ) => {
// 點擊No后,調用者收到'i choice no'數據
        this._reject('i choice no');
  }

  onYes = ( ) => {
// 點擊Yes后,調用者收到'i choice yes'數據
        this._resolve('i choice yes'); 
   } 

 

  探討4:如何銷毀無用的組件實例及其內存

    點擊完成后,Modal用處就不大了。那我們要怎么銷毀這個組件實例,以在文檔里面移除domj結果,並回收這個實例的內存呢。答案是ReactDomunmountComponentAtNode函數

    因此,我們需要在 onYes( ) 和 onNo( ) 里面繼續添加相應的邏輯:

// 以onYes( ) 為例

onYes = ( ) => {
    this._resolve('i choice yes');
    // 銷毀
    ReactDom.unmountComponentAtNode( this._container );
    this._dom.remove( );       
}

// 其中:this._container 來自於這部分代碼
// static show( )
let div = document.createElement('div');
let _model = ReactDom.render(<Model , div);
_model._container = div;

// 其中:this._dom 來自於這部分代碼
// static show( )
let dom = document.querySelector('body').appendChild(div);
_model._dom = dom;

 

 

  完,歡迎補充和指出問題。完整代碼請參考上面地址 O(∩_∩)O

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM