React前端开发总结

最近做了点前端工作,这里做个小结。

组件重构

对modelCom进行了重构,整个过程如下:

切分组件

先从大处切,将整个页面切成2左右两部分:ModelCollection、ModelContent。其中ModelCollection中将表格上的按钮单独封装成ModelController;ModelContent中分割成ModelDetail、ModelQuantity、ModelRecord3个组件。

graph LR A(ModelCom) --> B(ModelCollection) A --> C(ModelContent) B --> D(ModelController) C --> E(ModelDetail) C --> F(ModelQuantity) C --> G(ModelRecord)

处理状态

使用rudex的原则:

  • 数据被被多个组件共享
  • 组件消失后,数据有必要继续存在

在这种原则下组件的状态控制,应该存放到state中,不存放redux

确定状态:

  1. 确定reducer中的InitState数据,modelList、currentModel。

    modelList: 将model中分不同路由获取的modelQuantity、record数据都存放在modelList中相应的model中,根据model中数据是否为undefined来判断是否向后台获取,这里通过redux对后台数据做了一个整合。

    currentModel:为ModelCollection中当前选中的model,其内容需要显示在ModelContent的3个子组件中。直接传时需要将ModelCollection中的数据传递给父组件,再传递给兄弟组件ModelContent,再传递给个侄子组件。原来实现中,这3个组件都连接了redux,直接获取更方便一些。

  2. 确定action,包括获取modelList、获取modelContent等几个数据

组件实现

从切分的大组件开始重构,然后对弹窗(Modal)、按钮等小组件进行重构。

过程确实很酸爽,大组件只是显示比较大,内容相对少一些,操作主要集中在弹窗等小组件上。

Modal使用总结

这里涉及到另外一个任务,显示logbook列表,可以创建新的logbook,也可以修改logbook,点击logbook还可以看到对应的表格。表格组件已经封装妥当,主要是这个Modal的使用上。

记录

  • 构思

    logbook的列表是一个组件,这里称它LogbookList组件,创建、修改的弹窗是LogbookModal组件,对应的表格这里不涉及。

    这里只是父子组件的关系,不采用redux。

    LogbookModal中创建与更新服用一个组件,通过判断是否传递给要更新data来判断更新,还是新建。

    LogbookModal中需要套一个Form子组件,来进行表单的填写工作。

  • Modal的visible的控制

    visible采用了常规的render方式,在LogbookList中通过render控制LogbookModal的显隐。

  • Modal生命周期

    Modal的visible不会造成,componentWillUnmount的调用。 但是render时候用三目运算? :时,会造成不被刷新的那个组件unmount,父组件的unmount会引发children的unmout。

    这就是说,当通过visible方式调用Modal,并回填数据时,需要使用componentDidUpdate来刷新组件数据。

  • 如何获取Form中的数据?

    数据获取有2种简单的方式:

    1. 通过ref的form调用validateFields()去获取

    ​ const value: any = await this.form.current?.validateFields(); 来获取数据,其对象的field为Form.Item的name,value就是设置的数据。

    1. 通过form的onFinish(fieldsValue: Object)去获取
  • Form的数据如何回填?

    回填数据主要是用于修改,在创建与修改用相同窗口的情况下,通过判断是否传入要修改的数据,来判断创建还是更新。

    2种方法:

    1. 在Form上使用initialValues,来设置对象(与onOK的对象相同),或者在Form.Item上使用initialValue。

      这种方法的设置,需要结合Modal中添加destroyOnClose(关闭时销毁子元素)。因为initialValue只在创建时候设置一次,如果不销毁,则不会发生改变。

    2. 使用this.form.current?.setFieldsValue(obj)

      这里的form是Form通过ref引出,这种方式使用过程中也存在一点问题,即:componentDidMount()或者componentDidUpdate()中使用时,由于第一次的current=null,造成回填不成功。同样需要initValues结合使用。

    注意:Form.Item的children(如Input、Select)中设置defaultValue是无效的。

  • 重置Form

    this.form.current?.resetFields(); 避免再次打开时,数据还存在。

  • 子组件上点击事件只相应子组件,阻拦父组件点击事件

    在子组件上的onClick(e)上增加e.stopPropagation();即可。

    这里有preventDefault与stopPropagation的区别: 参考

    preventDefault,当event发生时,阻止浏览器默认的action

    stopPropagation,阻止event构建event chain,会组织父组件相应的事件响应。

回顾

回过头来看整个过程:

  • LogbookModal的数据更新操作都是由父组件LogbookList中完成

    创建在在LogbookList中发送api,并且刷新了LogbookList的列表;

    修改时,需要先将数据传入,修改的操作也是在父组件中进行,完成后更新父组件的数据;

  • LogbookModal的显隐方式给父组件LogbookList添加了2个state

    添加一个visible控制显隐,一个toUpdateData传入要修改的数据,如果这个父组件弹窗多,那这个代价是不能忽视的。而且用render方式,会造成父组件的其他子组件也跟着刷新,浪费性能。

优化

基于3个问题进行优化:

  1. Modal应不应该与父组件分离?

    在这种实现中,显然放在一起更省力,父子组件之间交互可以省略。

    但如果Modal过多,都写在一起,可维护性太差,所以还是分离比较好。

    分离的话,需要优化Modal的显隐,以及Modal返回数据的处理

  2. Modal的显隐控制是否可以提高?

    这篇文章:Modal实践 提供了2种思路,用于Modal的显隐控制由外部的props变为内部的state。

    • 将子组件Modal用ref引出,在父组件需要显示的时候调用ref.current?.show();
      这种方式使用在官网中有明确的说明,要避免使用。

    • 通过pub、sub的方式来实现。
      在父组件中创建该Observer,然后通过props传递给子组件,然后就可以通过父组件pub让子组件显示数据了。
      注意: 在componentDidMount()中sub,在componentWillUnmount()中unsub。

      经测试,在父子组件之间、兄弟组件之间都可以用这种方式来传递数据。如果配合context使用,使用效果会更方便一些。

      Observer实现如下:

      type CallBack = (payload: any) => void;
      export class Observer {
        private map = new Map<string, CallBack[]>();
      
        public sub(topic: string, callback: CallBack) {
          if (this.map.has(topic)) {
            this.map.get(topic)?.push(callback);
          } else {
            this.map.set(topic, [callback]);
          }
        }
        public pub(topic: string, playload?: any) {
          let callbacks = this.map.get(topic);
          if (callbacks && callbacks.length > 0) {
            for (const callback of callbacks) {
              callback(playload);
            }
          }
        }
        public unsub(topic: string, callback: CallBack) {
          if (this.map.has(topic)) {
            let callbacks = this.map.get(topic) || [];
            let index = callbacks.findIndex((cb) => cb === callback);
            callbacks.splice(index, 1);
          }
        }
      }
      
  3. Modal如何更新父组件数据更好一些?

 Modal可以提供统一的接口,表达操作以及操作的结果,父组件根据这个接口,进行数据的操作。数据操作常用的就是:create、update、delete等。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×