资讯专栏INFORMATION COLUMN

React实践 - Component Generator

null1145 / 1136人阅读

摘要:但是把这些组件串起来也有很多逻辑,如果每个类似的模块都重复写,任何一个小的逻辑发生变化,都可能需要修改所有的模块实现。接下来我们是的类似的,也是通过和来唯一地标识一个模块。当然每个模块可能会有不同的,这个也可以通过的入参来设置。

我们开发一个新产品的时候,通常会先抽象出一些公用的组件,然后通过这些组件来拼装成页面。不知道大家有没有发现,这种开发方式带来的问题是一个团队内经常会有这样的场景:

A 已经开发了一个 XX 表格模块,B 要开发一个类似的 YY 表格模块,然后 B 通常是去把 A 的代码 copy 一下,修改一些东西;或者不巧 B 不知道 A 已经开发 XX 表格,然后 B 又得一行行的写一些类似的代码。

造成这种问题的原因简单的说就是:组件抽象的粒度太单一。接下来我们会通过两个例子来讲述问题及我们如何解决这样的问题的。

一个简单的组件 - Switch

首先我们看一个简单的 Switch 组件,如果一个产品中有常用的两种切换功能:

如果使用之前封装的基础组件组件 Switch 来实现,我们需要如下调用:


 趋势
 列表

这种组件抽象方式(实现省略)好处就是通用性强,但带来一些问题:

每个人都需要维护选项的展示名称和顺序之间的关系

调用代码较长,有冗余

于是,我们对这类组件进行了重构,希望让每个组件使用更加简单,只需要关系具体的状态即可。具体的做法就是开发一个 Generator —— generateSwitch 来生成常用的切换组件:

export const generateSwitch = (name, options) => {
  const propTypes = {
    className: PropTypes.string,
    activeKey: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
    onChange: PropTypes.func.isRequired,
  };

  const Switch = (props) => {
    ...

    return (
      
        {
          options.map((entry, index) => (
            ...
          ))
        }
      
    );
  };

  Switch.propTypes = propTypes;
  Switch.displayName = name;

  return Switch;
};

export const ASwitch = generateSwitch("ABSwitch", [
  { name: "AA", key: "a" },
  { name: "BB", key: "b" },
]);

export const BSwitch = generateSwitch("CDSwitch", [
  { name: "CC", key: "c" },
  { name: "DD", key: "d" },
]);

这种做法就可以解决上面说的问题:

比常见的切换组件使用更加便利,调用代码一行就够了,而且能够起到统一参数的作用;

对外暴露生成函数 generateSwitch 也能保证通用性。

更复杂的例子 - 业务模块

下面以一个表格业务为例,常见的表格模块如下:

在开发这个模块的时候,虽然每个小区块我们都抽取了相应的组件,如 Selector, Table, Switch, Pagination 等。但是把这些组件串起来也有很多逻辑,如果每个类似的模块都重复写,任何一个小的逻辑发生变化,都可能需要修改所有的模块实现。所以这时候我们想做的事情就是:这个模块本身也是一个组件,我们只需要通过一些配置生成不同的模块,而不是重复的 copy 代码,然后修改一些差异的地方。

在这里碰到的一个问题是,我们整个系统是使用 Redux 来管理数据的,整个项目的 Store 结构如下:

每个业务模块会去 connect 相应的数据以及 actions ,每个模块都有相应的 reducer。并且每个卡片的 action 也需要做到全局唯一,所以我们给模块的 UI Component 以及 reducer 分别开发了相应的 Generator

首先来看 UI Component 的 Generator

function generateAbcModule({pageName, moduleName}) {
  const ACTION_PREFIX = `${pageName}/${moduleName}`;
  const LOAD = ACTION_PREFIX + "LOAD";
  ...

  function load(url, params, id) {
    return (dispatch, getState) => {
      const state = getState();
      ...

      return dispatch({
        type: LOAD,
        ....
      });
    };
  }

  @connect((state, props) => {
    const moduleState = state[pageName][moduleName];

    return {
      ...moduleState,
    };
  }, {
    load,
  })
  class AbcModule extends Component {
    ...
  }

  return AbcModule;
}

通过代码发现,我们把 actionCreators 与 UI 放在了一起,然后通过 pageNamemoduleName 来唯一地标识一个模块,拼装这两个参数作为 action 的前缀,从而达到每个模块的 action 是全局唯一的。

接下来我们是 reducerGenerator

function generateAbcModuleReducer({pageName, moduleName, defaultIndexes}) {
  const ACTION_PREFIX = `${pageName}/${moduleName}/`;
    const LOAD = ACTION_PREFIX + "LOAD";

    const initialState = {
      indexes: defaultIndexes,
      ...
  };

  return function AbcModuleReducer(state = initialState, action) {
    switch (action.type) {
      case LOAD:
        return {
          ...state,
          isLoading: true,
          ...
        };
      ...
    }
  };

类似的,reducer Generator 也是通过 pageNamemoduleName 来唯一地标识一个模块。当然每个模块可能会有不同的 initialState,这个也可以通过 generateAbcModuleReducer 的入参来设置。

总结

上面这种使用 Generator 来封装业务模块的方法,能够在一定程度上减少重复代码,加快开发速度,不过如果业务发展的很快,有可能会导致业务模块组件 props 泛滥 的问题。

以上面的排行卡片为例,可变的东西就非常多,相应的就需要很多的 props 来配置,所以我们也需要根据具体的业务来把握是否要进行抽象。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79720.html

相关文章

  • 精读《Epitath 源码 - renderProps 新用法》

    摘要:精读源码一共行,我们分析一下其精妙的方式。更多讨论讨论地址是精读新用法如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读帮你筛选靠谱的内容。 1 引言 很高兴这一期的话题是由 epitath 的作者 grsabreu 提供的。 前端发展了 20 多年,随着发展中国家越来越多的互联网从业者涌入,现在前端知识玲琅满足,概念、库也越来越多。虽然内容越来越多,但作为个体的...

    Magicer 评论0 收藏0
  • React 同构实践与思考

    摘要:后面会利用这个框架来做实践。接下来就是我们要继续探讨的同构同构数据处理的探讨我们都知道,浏览器端获取数据需要发起请求,实际上发起的请求就是对应服务端一个路由控制器。是有生命周期的,官方给我们指出的绑定,应该在里来进行。 众所周知,目前的 WEB 应用,用户体验要求越来越高,WEB 交互变得越来越丰富!前端可以做的事越来越多,去年 Node 引领了前后端分层的浪潮,而 React 的出现...

    MageekChiu 评论0 收藏0
  • React 单元测试策略及落地

    摘要:写好的单元测试,对开发速度项目维护有莫大的帮助。我认为单元测试的上下文存在于敏捷中。接下来一小节,就可以正式进入如何做的环节了如何写好单元测试。前面说到,我们对单元测试寄予 写好的单元测试,对开发速度、项目维护有莫大的帮助。前端的测试工具一直推陈出新,而测试的核心、原则却少有变化。与产品代码一并交付可靠的测试代码,是每个专业开发者应该不断靠近的一个理想之地。本文就围绕测试讲讲,为什么我...

    nifhlheimr 评论0 收藏0
  • ES6中文手册、ES6 Cheatsheet

    摘要:盲目使用替换后可能会导致预期意外的结果。在中,许多种方法来处理函数的参数默认值,参数数量,参数命名。此外,处理后的值,无论是解决还是拒绝的结果值,都是不可改变的。 这是一个 ES2015(ES6) 的Cheatsheet,其中包括提示、小技巧、最佳实践和一些代码片段,帮助你完成日复一日的开发工作。 Table of Contents var 与 let / const 声明 代码执行...

    Cristalven 评论0 收藏0
  • ES6 —项目综合实战(完结篇)

    摘要:模块化以项目中普遍会有的文件为例,实现导出再在其他文件中通过实现导入导入模块使用统一的模块化规范,可以提高代码的可读性,更易于维护。类操作先实战创建一个类在中创建实例中的类能让我们可以用更简明的语法实现继承,也使代码的可读性变得更高。 上一篇通过TodoList的练习,目的是为了让大家理解ES6中各种新特性的实际用途。 最好的学习方法就是实践,所以这节课结合实际项目,来更好的理解和掌握...

    izhuhaodev 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<