React之高阶组件与反向继承

React很适合构建大应用,除了它灵活的 JSX 语法外还有个很重要的缘由,就是它的高阶组件和组件的反向继承。咱们在构建大型应用中,高阶组件和反向继承让不少业务场景很高效的实现。开发中咱们确定会将UI组件库,从新封装成符合要求设计的组件,供你们统一调用;以及咱们在开发过程当中也会存在UI组件库迁移,两个库的组件名称以及API名称存在不一致状况,这就须要咱们将要组件名称和API名称进行重写了;固然最重要的一个业务场景是,咱们的项目完成度很高时,已经有不少节点调用的一个复杂组件,须要本身在它的基础上进行一些拓展和重写,但又存在牵一发而动全身的风险,这时咱们的高阶组件和反向继承就发挥了巨大的做用。node

高阶组件

React 对高阶组件的官方解释就是高阶组件是:参数为组件,返回值为新组件的函数。
咱们在构建应用中,用高阶组件(属性代理模式)实现了装饰模式(了解下 ES7 中的装饰器)。react

业务一:组件的二次封装

antd 组件库进行重写,使它的组件库风格符合设计要求。
index.js 调用bash

import MyButton from 'myButton.js'
import MyTree from 'myTree.js'
export {
    MyButton,
    MyTree
}
复制代码

咱们将 antd 的 Button 封装成咱们本身的 MyButton 样式风格。colors 相似于 antd 中的 type,特性有 primary、danger等颜色。下面只写colors 为 danger 的 Button。antd

import 'myButton.less'
export default class MyButton extends Component {
    render() {
        let { onClick, colors, className, ...others } = this.props
        return (
            <Button 
                className=`${className} my-button-wrap button-${colors}`
                onClick={(e) => {
                    onClick(e)
                }
                {...others}
            >
            </Button>
        )
    }
}
复制代码

myButton.less 部分实现app

.button-danger {
    background-color: #e14c46;
    color: #fff;
    &:hover {
	  background-color: #d7332c;
	  color:#fff;
    }
    &:disabled {
      background-color: #F19B96;
      color: #F2F2F2 ;
      &:hover {
        color: #F2F2F2 ;
        background-color: #fdcac6;
      }
    }
  }
复制代码

业务二:组件的项目迁移

咱们在项目中,遇到框架迁移是会有 API 名称不一样的问题。咱们将 antd 中树形控件的 API,改写成咱们以前项目中的 API。好比,咱们将以前的 onExpand 改写成 myExpand。固然,咱们还能够在里面加入更加复杂的功能,这就要依据咱们的业务须要来肯定功能了。这里只是进行演示,因此尽可能简单。框架

const { TreeNode } = Tree;
export default class MyTree extends Component {
    render() {
        let { myClick, myExpand, ...others } = this.props
        return (
            <Tree
                onCheck={() => {
                    myCheck()
                }
                onExpand = {(expandedKeys, {expanded: bool, node}) => {
                    myExpand(expandedKeys, {expanded: bool, node})
                }
                {...others}
            >
            </Tree>
        )
    }
}
MyTree.myTreeNode = TreeNode
复制代码

业务三:对以前的组件进行装饰

固然咱们有时写一个业务,彻底能够在,以前的组件的基础上,额外进行一些修饰,就能够知足需求,用装饰器的方式,就能够节省不少的代码。这样高阶组件就起到很重要的做用。(装饰器装饰器是一个实验性的 JavaScript 提案。)less

import OldComponent from "./Component.js"
const newComponent = (WrapComponent) => {
    
    return class extends WrapComponent {
        render() {
            let props = this.props
            let newProps = {
                @click:() => {},
                ...this.props
            }
            return <WrapComponent />
        }
    }
}
export default newComponent(OldComponent)
复制代码

业务四:工厂模式实现

获得程序的需求前,将实现方法相同的部分抽出,写入到高阶函数内部。而后,函数中以不一样的组件,以及不一样的函数为参数,返回功能类似但彻底不一样的组件。函数

function hocFn({
    WrappedComponent, 
    data, 
    callback = () => console.log('没有传入回调函数')
}) {
  // ...并返回另外一个组件...
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        data: data
      };
    }

    componentDidMount() {
      callback()
    }

    render() {
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}
//建立不一样表格()
let TreeTable = hocFn(Tree, data1, () => console.log('建立一个树形表格'))
let EditTable = hocFn(Form, data1, () => console.log('建立一个可编辑表格'))
复制代码

高阶组件之反向继承

反向继承最核心的两个做用,一个是渲染劫持,另外一个是操做state吧。反向继承有两种写法,两种写法存在不一样的能力。ui

实现一个子组件 ComponentChild.jsthis

export default class ComponentChild extends Component {
  constructor(props) {
    super(props)
    this.state = {
      num: 2019
    }
  }
  componentDidMount() {
    console.log("child component Did Mount")
  }
  clickComponent() {
    console.log("Component click")
  }
  render() {
    return (
      <div>{this.state.num}</div>
    )
  }
}
复制代码

装饰组件直接传入

imoprt ComponentChild from './ComponentChild.js'
let iihoc = WrapComponet => class extends WrapComponet {
    constructor(props) {
        super(props)
        this.state = {
            num: 2000
        }
    }
    componentDidMount() {
        console.log('iihoc componentDidMount')
        this.clickComponent()
    }
    return (
        <div>
            <div onClick={this.clickComponent}>iiHoc 点击</div>
            <div><WrapComponent /></div>
        </div>
    )
}
export default iihoc(ComponentChild)
复制代码

这样的方式比属性代理好的是,在外部组件能够调用被继承组件的方法。但不能将被继承的 state 和 钩子覆盖掉。

调用 super.render()

imoprt ComponentChild from './ComponentChild.js'
let iihoc = WrapComponet => class extends WrapComponet {
    constructor(props) {
	    super(props)
	    this.state = {
            num: 2000
	    }
    }
    componentDidMount() {
        console.log('iihoc componentDidMount')
        this.clickComponent()
    }
    return (
        <div>
            <div onClick={this.clickComponent}>iiHoc 点击</div>
            <div>{super.render()}</div>
        </div>
    )
}
export default iihoc(ComponentChild)
复制代码

这样的方式,外部组件的 state 能够将,被继承组件的 state 和 钩子函数完全覆盖掉。同时,外部组件也能够调用被继承组件的方法。

render() {
    let renderchild = super.render();
    let newProps = {
        ...renderTree.props,
        onClick: this.handleClick
    };
    const newRenderchild = React.cloneElement(
        renderchild,
        newProps
    );
    return newRenderchild;
}
复制代码

解决嵌套地狱(HOOK)

高阶组件、render props 等其余抽象层组成的组件会造成“嵌套地狱”。在 React16.8 中的HOOK是对嵌套地狱的一种解决方式吧。

相关文章
相关标签/搜索