歡迎指導與討論 : )
前言
文章的最后能寫出以 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可以使用ReactDom的render函數去完成: 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結果,並回收這個實例的內存呢。答案是ReactDom的unmountComponentAtNode函數
因此,我們需要在 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