• home > webfront > ECMAS > react >

    React项目中常见的技术坑与优化及Component Generator

    Author:zhoulujun@live.cn Date:

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

    react有什么坑点


    1、JSX做表达式判断时候,需要强转为boolean类型

    render() {
      const b = 0;
      return<div>
        {
          !!b && <div>这是一段文本</div>
        }
      </div>
    }

    如果不使用 !!b 进行强转数据类型,会在页面里面输出 0。

    2、尽量不要在 componentWillReviceProps 里使用 setState,如果一定要使用,那么需要判断结束条件,不然会出现无限重渲染,导致页面崩溃。(实际不是componentWillReviceProps会无限重渲染,而是componentDidUpdate)

    3、给组件添加ref时候,尽量不要使用匿名函数,因为当组件更新的时候,匿名函数会被当做新的prop处理,让ref属性接受到新函数的时候,react内部会先清空ref,也就是会以null为回调参数先执行一次ref这个props,然后在以该组件的实例执行一次ref,所以用匿名函数做ref的时候,有的时候去ref赋值后的属性会取到null。详情见

    4、render函数里面,对于UI事件,不要使用匿名函数

    class Demo {
      render() {
        return<button onClick={(e) => {
          alert('我点击了按钮')
        }}>
          按钮
        </button>
      }
    }

    由于onClick使用的是匿名函数,所有每次重渲染的时候,会把该onClick当做一个新的prop来处理,会将内部缓存的onClick事件进行重新赋值

    4、遍历子节点的时候,如果需要对列表进行操作,不要用 index 作为组件的 key 进行传入。具体参看《 vue、react中key有何区别?循环渲染优化与Vue.delete删除》


    react项目优化

    组件封装Component Generator

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

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

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

    一个简单的组件 - Switch

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

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

    <Switch
     className="switch"
     activeIndex={this.state.activeIndex}
     onChange={::this.handleSwitchChange}
    >
     <SwitchItem>趋势</SwitchItem>
     <SwitchItem>列表</SwitchItem>
    </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 代码,然后修改一些差异的地方。

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

    接下来我们是 reducer 的 Generator

    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 也是通过 pageName 和 moduleName 来唯一地标识一个模块。当然每个模块可能会有不同的initialState,这个也可以通过 generateAbcModuleReducer 的入参来设置。

    总结

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

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


    转载本站文章《React项目中常见的技术坑与优化及Component Generator》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/jsBase/2016_0817_7882.html