React的高阶组件和mixin

作者: 爱吃芋圆的小w | 来源:发表于2019-06-08 23:18 被阅读13次

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.高阶组件

  1. 高阶组件(higher-ordercomponent),取代mixin,类似于高阶函数,它接受 React 组件作为输入,输出一个新的 React 组件。
  2. 实现高阶组件的方法有如下两种:
  • 属性代理(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。

相关文章

网友评论

    本文标题:React的高阶组件和mixin

    本文链接:https://www.haomeiwen.com/subject/biruxctx.html