zoukankan      html  css  js  c++  java
  • 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

     

     

     

  • 相关阅读:
    Unity c# 状态机的简单入门
    python实战教程之自动扫雷(自己存下来学习之用)
    Kubernetes的三种外部访问方式:NodePort、LoadBalancer和Ingress-十一(1)
    Ubuntu安装eclipse以及创建快捷方式
    Dockerfile-HEALTHCHECK指令
    各个版本Microsoft Visual C++运行库下载
    docker 远程连接设置
    centos7安装redis3.2.12
    Windows下允许redis远程访问
    UltraISO制作U盘启动盘-centos7
  • 原文地址:https://www.cnblogs.com/BestMePeng/p/React_modal.html
Copyright © 2011-2022 走看看