使用场景

高阶组件的使用场景主要有以下 4 种:

  1. 操纵 props

  2. 通过 ref 访问组件实例

  3. 组件状态提升

  4. 用其他元素包装组件

每一种使用场景通过一个例子来说明。

操纵props

在被包装组件接收 props 前,高阶组件可以先拦截到 props,对 props 执行增加、删除或修改的操作,然后将处理后的 props 再传递给被包装组件。6.1 节中的例子就属于这种情况,高阶组件为 WrappedComponent 增加了一个 data 属性。这里不再额外举例。

通过ref访问组件实例

高阶组件通过 ref 获取被包装组件实例的引用,然后高阶组件就具备了直接操作被包装组件的属性或方法的能力。

function withRef(WrappedComponent) {
    return class extends React.Component {
        constructor(props) {
            super(props);
            this.someMethod = this.someMethod.bind(this);
        }

        someMethod() {
            this.wrappedInstance.someMethodInWrappedComponent();
        }

        render() {
            // 为被包装组件添加 ref 属性,从而获取该组件实例并赋值给 this.wrappedInstance
            return <WrappedComponent ref={(instance) => {this.wrappedInstance = instance }} {...this.props} />
        }
    }
}
javascript

当 WrappedComponent 被渲染时,执行 ref 的回调函数,高阶组件通过 this.wrappedInstance 保存 WrappedComponent 实例的引用,在 someMethod 中,通过 this.wrappedInstance 调用 WrappedComponent 中的方法。这种用法在实际项目中很少会被用到,但当高阶组件封装的复用逻辑需要被包装组件的方法或属性的协同支持时,这种用法就有了用武之地。

组件状态提升

在第 2 章中已经介绍过,无状态组件更容易被复用。高阶组件可以通过将被包装组件的状态及相应的状态处理方法提升到高阶组件自身内部实现被包装组件的无状态化。一个典型的场景是,利用高阶组件将原本受控组件需要自己维护的状态统一提升到高阶组件中。

function withControlledState(WrappedComponent) {
    return class extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                value: ''
            };
            this.handleValueChange = this.handleValueChange.bind(this);
        }

        handleValueChange(event) {
            this.setState({
                value: event.target.value
            });
        }

        render() {
            // newProps 保存受控组件需要使用的属性和事件处理函数
            const newProps = {
                controlledProps: {
                    value: this.state.value,
                    onChange: this.handleValueChange
                }
            };
            return <WrappedComponent {...this.props} {...newProps} />
        }
    }
}
javascript

这个例子把受控组件 value 属性用到的状态和处理 value 变化的回调函数都提升到高阶组件中,当我们再使用受控组件时,就可以这样使用:

class SimpleControlledComponent extends React.Component {
    render() {
        // 此时的 SimpleControlledComponent 为无状态组件,状态由高阶组件维护
        return <input name="simple" {...this.props.controlledProps } />
    }
}

const ComponentWithControlledState = withControlledState(SimpleControlledComponent);
javascript

用其他元素包装组件

我们还可以在高阶组件渲染 WrappedComponent 时添加额外的元素,这种情况通常用于为 WrappedComponent 增加布局或修改样式。

function withRedBackground(WrappedComponent) {
    return class extends React.Component {
        render() {
            return (
                <div style={{backgroundColor: 'red'}}>
                    <WrappedComponent {...this.props} />
                </div>
            )
        }
    }
}
javascript