React的高阶组件和mixin

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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • 事件系统 合成事件的绑定方式 Test 合成事件的实现机制:事件委派和自动绑定。 React合成事件系统的委托机制...
    cheneyg916阅读 380评论 0 1
  • 组件抽象指的是让不同组件公用同一类功能,可以说成组件功能复用,在不同的设计理念下,有许多抽象方法,而对于React...
    GIScyw阅读 729评论 0 0
  • 在目前的前端社区,『推崇组合,不推荐继承(prefer composition than inheritance)...
    Wenliang阅读 77,648评论 16 125
  • 一、mixin 什么是mixin:创造一种类似多重继承的效果。事实上,说它是组合更为贴切。 1.封装mixin方法...
    南慕瑶阅读 2,084评论 0 0
  • React进阶之高阶组件 前言 本文代码浅显易懂,思想深入实用。此属于react进阶用法,如果你还不了解react...
    流动码文阅读 1,182评论 0 1