Props是React组件的参数,而一个组件还可能拥有其内部状态。这里的状态是抽象的状态,不仅仅指state(欢迎补充例子)。
Props可以由外部调用者改变,但是组件自己不能改变自己接收到的Props,虽然组件可以监听Props的改变。相反,组件可以改变自己的状态,而外部调用者却不应该直接改变组件内部状态。
在传统的对象模型里,对象间可以互相传递消息,消息接收者受控地改变状态。当然,React组件也可以定义方法,但是尽量不要这样做,因为React整个体系是由数据流(props流)驱动的,而一个通过消息(方法调用)来驱动的组件,会破坏整个体系,导致同步和适配工作、项目复杂度大增。比如,做代理组件,props很容易向下传递,而methods却需要重新定义。再比如,在container模式,如果一个UI组件是由方法驱动的,那么将很难将其封装为container,因为composer只负责中介数据,却很难维持组件实例的引用。
综上,我们希望构建没有公共方法,仅受props控制的组件。
在其他props不变的情况下,组件的一个prop应该和其一组状态有一个一一映射关系。
纯属性,即不控制任何内部状态。
受控属性,即该prop和组件的部分状态始终按某种一一映射保持一致。比如,prop是userId,而内部状态包含这个用户的nickname和email,则userId不变,nickname,email不变,userId变,nickname,email按userId改变(不是随便变)。
半受控属性,相比于受控属性,prop变化时,对应的状态会更新为对应状态,但是在prop未改变时,这些状态却可以自由变化。比如prop是postId,状态是content,则content可能受到自由的编辑,但是postId改变时,content又会被更新为与新postId一致。
一次性属性,相比于受控属性,状态被设置到与该prop同步,只在组件mount时发生一次。
理论上,我们希望保持组件越纯越好,能做成纯组件的,尽量做成纯组件,不能的,至少也要做成受控组件。但是在实际的生产实践中,由于一些原因,我们还是被迫接受了制作半受控组件和一次性组件,比如封装某些非react组件,抑或某些操作类组件内部状态过多,处于性能考虑或其他bug,不便于频繁与应用状态保持同步。
比如说,对一个本身内容可自由变化的文本编辑器进行封装,其content很可能被设计为半受控属性,如此一来,虽然如果编辑器内容改变,而prop强行保持不变,会导致不一致问题,但大部分实际用例却是另一番景象:编辑器内容改变->触发onChange事件->prop改变->prop与编辑器内容一致,因此不需要更新编辑器内容。这样效率高,而且也保持了正常用例下的状态同步,在实际生产实践中是可取的。相比较,如果强行制作为受控组件,则需要附加很tricky的代码,增加复杂度,牺牲性能。
再比如,封装一些带options的组件,通常这个options会被设计为一次性参数,因为这些options的在原始组件中的设计初衷就是在组件创建时进行初始化设置,并没有要求组件在创建后还可以通过update options等类似方式来更新组件,在正常用例下,是没有什么问题的。如果强行做成半受控及其以上属性,则在options更新时,该组件实际上也必须销毁重构,而且,options的等价比较有时也并不简单,比如包含类对象或函数。因此,若非有特殊需求,这类属性做成一次性属性即可。
说了这么多,在实战中,还是要根据具体情况灵活变通,关键是,知道的越多,办法越多越好。