1. 组件间抽象
1.1.mixin
1.对于广义的 mixin方法,就是用赋值的方式将mixin对象里的方法都挂载到原对象上,来实现对对象的混入 。我们可以试着封装一下mixin方法。
const mixin = function(obj, mixins) {
const newObj = obj;
newObj.prototype = Object.create(obj.prototype);
for (let prop in mixins) {
if (mixins.hasOwnProperty(prop)) {
newObj.prototype[prop] = mixins[prop];
}
}
return newObj;
}
const BigMixin = {
fly: () => {
console.log('I can fly');
}
};
const Big = function() {
console.log('new big');
};
const FlyBig = mixin(Big, BigMixin);
const flyBig = new FlyBig(); // => 'new big'
上述的方法其实类似于用赋值的方式将 mixin 对象里的方法都挂载到原对象上,来实现对对象的混入。
2.在React中使用mixin
- React 在使用createClass构建组件时提供了mixin属性,比如官方封装的 PureRenderMixin
import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
React.createClass({
mixins: [PureRenderMixin],
render() {
return <div>foo</div>;
}
});
- ES6 Classes 与 decorator
然而,使用我们推荐的 ES6 classes 形式构建组件时,它并不支持 mixin。 而decorator(ES7新特性),正巧可以用来实现 class 上的 mixin。
(但是mixin具有很多问题,比如:破坏了原有组件的封装、命名冲突、增加复杂性等)
1.2.高阶组件
- 高阶组件(higher-ordercomponent),取代mixin,类似于高阶函数,它接受 React 组件作为输入,输出一个新的 React 组件。
- 实现高阶组件的方法有如下两种:
-
属性代理(props proxy)
将组件作为参数传递给高阶组件,高阶组件中的render 方法返回了传入 组件的React 组件。这样,我们就可以通过高阶组件来传递props,这种方法即为属性代理。它是常见高阶组件的实现方法,可以通过一个例子来实现:
//1.首先,我们创建一个高阶组件
import React, { Component } from 'React';
const MyContainer = (WrappedComponent) =>
class extends Component {
render() {
return <WrappedComponent {...this.props} />;
}
}
//2.使用这个高阶组件
import React, { Component } from 'React';
class MyComponent extends Component {
// ...
}
export default MyContainer(MyComponent);
//或者使用decorator 来转换
import React, { Component } from 'React';
@MyContainer
class MyComponent extends Component {
render() {}
}
export default MyComponent;
高阶组件替传入组件管理控制props里面一切属性,管理控制包括增,删,改,查。同时他自身还有自身的状态,即state,来强化传入组件。举个例子:
1.首先创建一个高阶组件
import React,{ Component } from 'react';
import '../../style/higherOrderComponent/higherOrderComponent.scss';
const AttributeAgentHigherOrderComponent2 = (BaseComponent) =>
class extends Component{
constructor(props){
super(props);
this.state = {
value:this.props.initValue || '',
}
}
onValueChange = (event) => {
let value = event.target.value.toString();
// 这句最直观的体现什么是受控(要什么值显示什么值)
value = `输入:${value === '输入' ? '' : value.replace('输入:','')}`;
this.setState({value:value});
}
render(){
const { value } = this.state;
const newProps = {
value: value,// input 的value属性
eventOnChange:{
onChange: this.onValueChange,// input的onChange监听,方法在高阶组件内
},
}
const props = Object.assign({},this.props,newProps);// 合成最新的props传给传入组件
return (
<BaseComponent {...props}/>
)
}
}
export default AttributeAgentHigherOrderComponent2;
2.然后创建一个受控组件
import React, { Component } from 'react';
import AttributeAgentHigherOrderComponent2 from './AttributeAgentHigherOrder';
class ControlInput extends Component{
// 一个受控组件,通过属性代理的方式,把控制逻辑放进高阶组件中。
render(){
const { value , eventOnChange} = this.props;
return (
<input value={value} {...eventOnChange}/>
)
}
}
export default AttributeAgentHigherOrderComponent2(ControlInput);
在上述例子中,创建了一个受控制的input组件,首先添加一个props到传入的组件中,这个props中有一个监听函数可以实时监听输入值的变化,然后重新设置state,最后将这个props与原先的props合并传给输入的参数组件中。
-
反向继承(inheritance inversion)
1.正如所见,高阶组件返回的组件继承于WrappedComponent。因为被动地继承了WrappedComponent,所有的调用都会反向,这也是这种方法的由来
2.在反向继承方法中,高阶组件可以使用WrappedComponent引用,这意味着它可以使用WrappedComponent的state、props、生命周期和render方法。但它不能保证完整的子组件树被解析
const MyContainer = (WrappedComponent) =>
class extends WrappedComponent {
render() {
return super.render();
}
}
举个简单的例子,这个例子的大致功能就是当输入框中有值时,就出现提交按钮,否则就消失。
1.首先,我们先创建一个传入的组件
import React,{ Component } from 'react';
class ReverseInput extends Component{
constructor(props){
super(props);
this.state = {
value:''
}
}
// 点击提交按钮
toSubmit = () => {}
// 监听输入框变化
valueChange = (eve) => {}
render(){
const { value } = this.state;
return (
<div>
<input onChange={this.valueChange} value={value}/>
<button onClick={this.toSubmit}>提交</button>
</div>
)
}
}
这个组件中的方法都是空的,我们需要在高阶组件中使用渲染劫持的方法进行方法的重写。渲染劫持指的就是高阶组件可以控制 WrappedComponent 的渲染过程,并渲染各种各样的结果。
2.创建一个高阶组件
const ReverseInherit1 = BaseComponent =>
class extends BaseComponent{ // 继承传入组件
// 在这里定义监听value值变化的函数
valueChange = (eve) => {
this.setState({value:eve.target.value})
}
// 在这里重写提交的函数
toSubmit = () => {
alert(`您要提交的值是:${this.state.value}`);
}
render(){
const { value } = this.state;
const superEle = super.render();// 拿到父组件的要渲染的结构对象,做渲染劫持的关键
const newElement = React.cloneElement(superEle,this.props,superEle.props.children);
if(value){
return (
super.render()
)
}else{// value 有值则对原来的结构进行调整
newElement.props.children.splice(1,1);
return (newElement)
}
}
}
在上述的例子中,高阶组件就是通过渲染劫持来改变输入组件渲染的内容,如果输入框有值时,就使用父组件(输入组件)的render,否则修改原来的render。