react 高阶组件HOC

831 阅读2分钟

定义:高阶组件是参数为组件,返回值为新组件的函数

几种强化组件的模式

单一组件 ---> 功能组件

image.png

image.png

image.png

image.png

组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。像 Redux 的 connect 和 Relay 的 createFragmentContainer。

基本使用

// 接收Cmp 返回新组件
const foo = Cmp => props => {
    return (
        <div className="customCmp">
            <p>自定义新增的组件</p>
            <Cmp {...props} />
        </div>
    )
}

function Child(props){
    return <div>Child {props.name}</div>
}

const Foo = foo(Child) // Child 原组件 Foo 新组件
// 其也可链式使用
const Foo2 = foo(foo(Child)) // 多层叠加
//  使用处 page
<Foo name="123" />

结合装饰器模式使用

要想实现这种写法,需要安装插件和配置。 npm install -D @babel/plugin-proposal-decorators, 具体可度一下

// 装饰器只能⽤在class上, 问题1: 装饰的顺序是啥?
// child 被foo 等装饰了
@foo2
@foo
@foo
class Child extends Components {
    render(){
        return <div>child</div>
    }
}

// use 已被装饰
<Child />

⚠️ 不要在 render ⽅法中使⽤ HOC

这不仅仅是性能问题 - 重新挂载组件会导致该组件及其所有⼦组件的状态丢失。 why?

render() {
    // 每次调⽤ render 函数都会创建⼀个新的 EnhancedComponent
    // EnhancedComponent1 !== EnhancedComponent2
    const EnhancedComponent = enhance(MyComponent);
    // 这将导致⼦树每次渲染都会进⾏卸载,和重新挂载的操作!
    return <EnhancedComponent />;
}

antd 表单案例

react 没有像vue 那种双向绑定的东西,所以在弄表单input 最初可能使用这样的形式:

class FPage extends Component {
    constructor(props) {
        super(props)
        this.state = {
            name: '',
            password: ''
        }
    }
    render(
        <div>
            <h3>state 自维护形式</h3>
            <Input placeholder="name" value={name} onChange={ e => {
                this.setState({name: e.target.value})
            }} />
            <Input placeholder="password" value={password} onChange={ e => {
                this.setState({password: e.target.value})
            }} />
        </div>
    )
}

使用antd里的Form.create() 进行装饰实现:

Form.create() 里常用的几个方法:

  • getFieldDecorator:⽤于和表单进⾏双向绑定
  • getFieldsValue:获取⼀组输⼊控件的值,如不传⼊参数,则获取全部组件的值
  • getFieldValue:获取⼀个输⼊控件的值
  • validateFields 校验相关
  • xxx 其他

2.x antd 使用 getFieldDecorator 包装的控件,会自动添加 value(或 valuePropName 指定的其他属性) onChange(或 trigger 指定的其他属性),数据同步将被 Form 接管

@Form.create()
class FPage2 extends Component {
    submit = () => {
        console.log("submit", getFieldsValue(), getFieldValue("name"));
    };
    render(){
        console.log("props", this.props.form);
        const {getFieldDecorator} = this.props.form;
        return (
            <div>
                <h3>FormPage2</h3>
                <Form>
                    <Form.Item label="姓名">
                        {
                            getFieldDecorator("name", {rules:[nameRules]})
                            (<Input placeholder="please input ur name" />) 
                        }
                    </Form.Item>
                    // xxxx
                 
                </Form>
            </div> 
            );  
    }
}

表达组件实现数据收集、校验、提交特性,可通过⾼阶组件扩展。

来看看这个Form.create、 getFieldDecorator 的大致实现

export default function kFormCreate(Cmp){
    return class extends Component {
        constructor(props){
            super(props)
            this.state = {}
            this.options = {}
        };
        handleChange(e){
            // input 的change 事件
            let {name, value} = e.target;
            this.setState({[name]: value});
        };
        getFieldDecorator(){
            // 扩张了Input 标签
            this.options[field] = option;
            return InputCmp => React.cloneElement(InputCmp, 
            { name: field, value: this.state[field] || "",
                onChange: this.handleChange // 控件 change 事件处理 
            });
        };
        getFieldsValue(){
            return {...this.state};
        };
        getFieldValue(name){
            return this.state[name];
        };
        validateFields(){};
        render(){
            // 实现了方法的扩展
            return 
            (
                <div className="cp">
                    <Cmp {...props} 
                        getFieldDecorator = {this.getFieldDecorator}
                        getFieldsValue = {this.getFieldsValue}
                        getFieldValue = {this.getFieldValue}
                        validateFields = {this.validateFields}
                    />
                </div>
            )
        }
        
    }
}

高阶组件可以做到哪些?

  1. 复用逻辑 (批量对原有组件进行加工,包装)
  2. 强化props & 抽离state (混入新的props)
  3. 条件渲染、控制渲染、懒加载
  4. 劫持事件
  5. 添加事件监听,日志; ......等等

antd源码

Antd 中的组件大部分基于蚂蚁金服的组件库 react-component. rc-form

rc-form 解析参考