React中父组件与子组件之间的数据传递和标准化的思考

  React中父组件与子组件之间的数据传递的的实现大家都可以轻易做到,但对比很多人的实现方法,总是会有或多或少的差异。在一个团队中,这种实现的差异体现了每个人各自的理解的不同,但是反过来思考,一个团队用了同样的UI,同样的框架,实现方式确实有差异,这其实就是工程化的问题。

  回到React中父组件与子组件之间的数据传递的问题上来。

  父组件与子组件之间的数据传递的实现方式大致可以分为2种情况:

    1、子组件用自己的flux环传递数据,最后调用父组件的onChange事件将数据传给父组件。

    2、子组件调用父组件的onChange事件,在父组件中的onChange事件中调用flux环传递数据到付组件的View层

  这2种方式各有优点,对比这两种方式,在实时性上,第二种方式是通过调用父组件的onChange事件,数据改变是通过父组件的flux环发生改变的,所以是实时的,而第一种方式的数据是通过子组件自己的flux环改变的,最终传给父组件,所以非实时;而在粒度上,这2种方式是相同的,都是维护了一个局部逻辑。我们一般用子组件来实现一些页面局部的复杂逻辑,如果这个逻辑内部的数据处理也很复杂(要根据各种情况作出不同的判定、改变),那么第一种无疑更为合适,而更为现实的情况是,往往局部的复杂逻辑的实现在子组件内部还有子组件的子组件,这种情况下,优先选择第一种方式;那么相对的如果这个数据处理比较简单,我们完全可以将整个子组件看做页面上的一个输入框(也可以是其他实现某个功能的对话框),那么父组件中的一个输入框调用父付组件的onChange事件来改变数据流,毫无疑问,第二种方式更为合理些。

  按照第二种方式,我们经常可以在父组件中看到如下的局部代码:

    onChange: function(property, value) {
        Action.changePromotionDetail(property, value);
    },

    render: function() {
        return (
            <div className="xui-promotion-flashSalePage">
                <ProductInfo onChange={this.onChange} />
                <PromotionInfo onSubmit={this.onSubmit}
                               onChange={this.onChange}
                               promotionName={this.state.promotionName}
                               advert={this.state.advert}
                               promotionRangeDate={this.state.promotionRangeDate}
                               memberGrades={this.state.memberGrades}
                               memberGrade={this.state.memberGrade}
                               promotionPrice={this.state.promotionPrice}
                               numPerPurchase={this.state.numPerPurchase}
                               period={this.state.period}
                               numPerPeriod={this.state.numPerPeriod} />
            </div>
        )
    }

  如果传入子组件的属性和方法越多,那么代码的可读性其实越差,维护起来也更不方便,甚至在父组件的Store中还需要对数据流做许多处理,类似:

    changePromotionDetail: function(action) {
        if (...) {
            this.data[action.data.property] = ...;
        } else {
            this.data[action.data.property] = action.data.value;
        }
        
        this.__emitChange();
    }

  那么我们又需要思考,如果我们像上面所说的将子组件看做一个输入框,一个输入框应该只需要输入一个值,父组件不应该接受那么多的不同属性的数据,同时父组件需要做的只是将接受到的一个数据通过flux环传递给父组件的View层。那么我们可以采用的一种方法是子组件的数据变更先调用子组件的onChange事件,并在其中做好必要的数据处理,然后将数据添加到一个对象中,以一个数据处理完毕的对象的形式调用父组件的onChange事件:

    onChange: function(property, value) {
        if (property == '...') {
            // do someting here...
        } else {
            // do someting here...
        }

        var obj = {
            // some key/value here
        };

        this.props.onChange(obj);
    }

  这样父组件中的代码就可以简化为如下:

    onChange: function(obj) {
        Action.changePromotionDetail(obj);
    },

    render: function() {
        return (
            <div className="xui-promotion-flashSalePage">
                <ProductInfo onChange={this.onChange} />
                <PromotionInfo onSubmit={this.onSubmit}
                               onChange={this.onChange}
                               infoObj={this.state.infoObj} />
            </div>
        )
    }

  再回到标准化的思考上,以React为例,React本身只是一个UI,facebook推荐了flux的架构,这就是一种实现方式,那么一个团队在大方向上采用的技术和实现方式其实就确定了下来。如果我们进一步的细分,对诸如父组件和子组件之间的数据传递的方式,数据传递的格式等等作出限制,看上去好像限制了研发编程的自由,同时将研发的工作变成了机械的重复,但是换个角度,这样的限制必然提高了研发效率,每个人碰到不同的场景都有了统一的应对策略;这样的限制也必然降低维护的成本,每个人都能理解他人的编程思路。这就是工程化的一部分。

  编程就是为了解决问题,要想解决问题又需要考虑2个问题:

    1、工具。

    2、方式。

  工具是学不完的,尤其现在前端的工具井喷式的发展(虽然侧面说明了前端工具的匮乏~),到这个公司用React,以后跳槽去了另一家又用Angular,后来换了部门,这个部门又用Vue。

  那么,作为研发,或许可以思考第二个问题,标准化的方式。每个公司每个部门都会有一套标准,你需要做的是先学会这套标准,同时去思考可以完善的地方,譬如这种场景没有标准化的解决方案,你可以提出一个方案,如果被大家接受了,你的方案就成为了标准的一部分。

  这或许也是一条前端之路,一条提高编程效率与维护性的路,一条解决工程化部分问题的路。