重学react——高阶组件

lihaixing

一、高阶组件基础

一句话:就是一个函数,传入一个组件,返回一个新组件;作用就是对传入的组件进行了增强

const lessons = [
    {stage: 'react', title: '组件化1'},
    {stage: 'react', title: '组件化2'},
    {stage: 'react', title: '组件化3'}
];

// 函数组件
function Lesson(props: any) {
    return <div>
        {props.stage} - {props.title}
    </div>;
}


const withContent = (Comp: any) => (props: any) => {
    const content = lessons[props.idx];
    return <Comp {...content}/>;
};
// 高阶组件
const LessonContent = withContent(Lesson);

// 高阶组件的运用
<LessonContent idx={2}/>

另外我们都知道ES6中的一种语法叫做装饰器,用在组件上和高阶组件也是一样的,只不过装饰器只能用在类组件中

const withContent2 = (Comp: any) => {
    return class extends React.Component<any, any> {
        render() {
            const content = lessons[this.props.idx];
            return <Comp {...content}/>;
        }
    };
};

@withContent2
class Lesson2Content extends React.Component<any, any> {
    render() {
        return <div>
            {this.props.stage} - {this.props.title}
        </div>;
    }
}
// 运用
<Lesson2Content idx={1}/>

二、利用高阶组件简单实现antd中的表单验证

1、先看一下antd用法

import { Form, Icon, Input, Button } from 'antd/es';

class HorizontalLoginForm extends React.Component {

    handleSubmit = e => {
        e.preventDefault();
        this.props.form.validateFields((err, values) => {
            if (!err) {
                console.log('Received values of form: ', values);
            }
        });
    };

    render() {
        const { getFieldDecorator} = this.props.form;
        return (
            <Form layout="inline" onSubmit={this.handleSubmit}>
                <Form.Item >
                    {getFieldDecorator('username', {
                        rules: [{ required: true, message: 'Please input your username!' }],
                    })(
                        <Input
                            prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
                            placeholder="Username"
                        />,
                    )}
                </Form.Item>
                
                <Form.Item>
                    <Button type="primary" htmlType="submit">
                        Log in
                    </Button>
                </Form.Item>
            </Form>
        );
    }
}

const WrappedHorizontalLoginForm = Form.create({ name: 'horizontal_login' })(HorizontalLoginForm);

2、我们实现一下Form.create

用kFormCreate代替Form.create
(1)

const kFormCreate = (Form: any) => {

    return (props: any) => {
        // state存储value键值对 name: value
        const [state, setState] = useState<any>({});
        // 存储存在的错误信息 name: errMsg
        const [errMsg, setErrMsg] = useState<any>({});
        // 存储当前验证的键值 name
        const validateKey = useRef('');
        // 存储各个键值的验证选项 name: {rules:[{required:..., ...}]}
        const options = useRef<any>({});
        
        // 表单change事件,改变state并触发单项校验
        const handleChange = (e: any) => {
            const {name, value} = e.target;
            // 表单change时,存储当前需要验证的键值
            validateKey.current = name;
            setState((state: any) => ({
                ...state,
                [name]: value
            }));
        };
        
        // 单项校验,依赖于options, state,但是主要依赖state变化,也就是表单value的变化,而options只有getFiledDec方法初始化表单时改变options(此时validateKey.current为空,因此走不到单项校验里)
        const validateFiled = useCallback((field: string) => {
            const {rules} = options.current[field];
            const value = state[field];
            const ret = !rules.some((rule: any) => {
                if (rule.required && !value) {
                    setErrMsg((err: any) => ({
                        ...err,
                        [field]: rule.message
                    }));

                    return true;
                }
                return false;
            });

            if (ret) {
                setErrMsg((err: any) => ({
                    ...err,
                    [field]: ''
                }));
            }

            return ret;
        }, [options, state]);


        useEffect(() => {
            // 监控 validateFiled 变化,一但变化就进行单项校验
            if (validateKey.current) {
                validateFiled(validateKey.current);
            }

        }, [validateFiled]);

        // 传入校验规则返回一个高阶组件
        const getFiledDec = (field: string, option: object) => {
            // 添加校验选项
            options.current[field] = option;
            
            // 高阶组件
            return (InputComp: any) => {
                return <div>
                    {
                        <!-- 克隆原有组件,方便重写属性和方法 -->
                        React.cloneElement(InputComp, {
                            name: field,
                            value: state[field] || '',
                            onChange: handleChange
                        })
                    }
                    {
                        <!-- 有错误时,显示错误信息 -->
                        !!errMsg[field] && <div style={{color: 'red'}}>{errMsg[field]}</div>
                    }
                </div>;
            };
        };
        
        // 提交校验(整体校验)
        const validateFileds = (cb: (res:any) => {}) => {
            const ret = Object.keys(options.current).every((key: string) => {
                return validateFiled(key);
            });
            if (ret) {
                cb(state);
            }
        };

        return <Form
            {...props}
            getFiledDec={getFiledDec}
            validateFileds={validateFileds}
        >
        </Form>;
    };
};

3、使用

const KForm = (props: any) => {
    const {getFiledDec, validateFileds} = props;
    const onSubmit = () => {
        validateFileds((res: any) => {
            console.log(res);
        });
    };

    return <div>
        {
            getFiledDec('username', {
                rules: [{required: true, message: 'Please input your username!'}]
            })(<Input type="text"/>)
        }
        {
            getFiledDec('password', {
                rules: [{required: true, message: 'Please input password!'}]
            })(<Input type="password"/>)
        }

        <Button onClick={onSubmit}>登录</Button>
    </div>;
};

const MyForm = kFormCreate(KForm);

export default () => {
    return <div>
        <MyForm/>
    </div>;
}
阅读 615

大前端
专注前端开发,分享技术难题,共同进步
31 声望
6 粉丝
0 条评论
你知道吗?

31 声望
6 粉丝
宣传栏