高阶组件就是一个函数,接收一个组件,返回一个新的组件,可以用来扩展传入的组件的功能

注:在高阶组件之前通过mixins来实现相同的功能,官方不推荐使用mixins

声明高阶组件

function higherOrderComponent (WrappedComponent) {
  return class extends React.Component {
    render () {
      return (
          <WrappedComponent />
      )
    }
  }
}

高阶组件的调用

const EnhancedComponent = higherOrderComponent(WrappedComponent);

举个例子,在组件里面注入属性传递日志

function logProps (WrapperComponent) {
  return class extends React.Component {
    componentWillReceiveProps (nextProps) {
      console.log(`当前属性`, this.props);
      console.log(`新的属性`, nextProps)
    }
    render () {
      return <WrapperComponent { ...this.props }/>
    }
  }
}

class MyComponent extends React.Component {
  constructor (props) {
    super(props);
  }
  render () {
    return (
        <h1>{ this.props.name }</h1>
    )
  }
}

const EnhanceComponent = logProps(MyComponent);

class App extends Component {
    state = {
        name: 'name',
        pwd: 'password'
    };
    handleClick = () => {
        this.setState({
            name: Math.random()
        })
    };
    render() {
        return (
            <div>
                <EnhanceComponent name={ this.state.name } />
                <button onClick={ this.handleClick }>click</button>
            </div>

        );
    }
}

注意事项

  • 高阶组件不应该改变原来的组件,而应该组合成一个新的组件,类似纯函数的思想
  • 不要在render方法里面使用高阶组件,应该再组件定义外部调用高阶组件
  • 如果组件有静态方法必须一同拷贝
// 组件定义静态方法
WrappedComponent.staticMethod = function() {/*...*/}
// 调用高阶组件函数
const EnhancedComponent = enhance(WrappedComponent);
// 返回的组件没有静态方法
typeof EnhancedComponent.staticMethod === 'undefined' // true

解决方法就是在高阶组件里面拷贝对应的具体静态方法

function enhance(WrappedComponent) {
  class Enhance extends React.Component {/*...*/}
  // 拷贝指定的静态方法
  Enhance.staticMethod = WrappedComponent.staticMethod;
  return Enhance;
}

已经有封装好的库来实现静态方法的拷贝hoist-non-react-statics

还有另一种方法,是把组件的静态方法export出来,例如

MyComponent.someFunction = someFunction;
export default MyComponent;

// 导出静态方法
export { someFunction };

// 使用时候导入相应的静态方法
import MyComponent, { someFunction } from './MyComponent.js';
  • props可以通过高阶组件传递,ref却不传递,如果给元素添加ref属性,ref引用的是最高层组件,而不是包装的组件

render props

render props通过过传递函数给子元素调用,该函数会返回需要渲染的元素或者组件

官方给的例子

class Mouse extends React.Component {
    constructor (props) {
        super(props);
        this.state = { x: 0, y: 0 };
        this.handleMouseMove = this.handleMouseMove.bind(this);
    }
    handleMouseMove (event) {
        this.setState({
            x: event.pageX,
            y: event.pageY
        });
    }
    render () {
        return (
            <div style={{ position: 'relative', width: '300px', height: '300px', border: '1px solid #333' }} onMouseMove={ this.handleMouseMove }>
                { this.props.render(this.state) }
            </div>
        )
    }
}

上面的render函数里面调用了this.props.render(this.state)方法,父组件通过render属性传入一个函数,该函数返回需要渲染的元素或组件,子组件调用父组件传入的函数,传入自身的状态来渲染

class Cat extends React.Component {
    render () {
        const mouse = this.props.mouse;
        return (
            <div>cat position is { mouse.x }, { mouse.y }</div>
        )
    }
}

声明一个Cat组件,显示Cat的位置

class App extends Component {

    constructor (props) {
        super(props);
        this.PosRender = this.PosRender.bind(this);
        this.CatRender = this.CatRender.bind(this);
    }

    PosRender (mouse) {
        return (
            <div>{ mouse.x }, { mouse.y }</div>
        )
    }

    CatRender (mouse) {
        return <Cat mouse={mouse} />
    }

    render() {
        return (
            <div>
                <Mouse render={ this.CatRender } />
                <Mouse render={ this.PosRender } />
            </div>
        );
    }
}

可以复用<Mouse />组件,父组件可以动态构建自己的渲染逻辑,prefect~