13

前言

最近在用React做项目,因为内容主要是增伤改查,就决定选择一个可以方便做增删改查的框架,经同事推荐,选择了ant-design。

但是在开发过程中遇到了各种困惑,一方面是因为对react本身的不熟悉,另一方面则是对ant-design框架的不熟。

因为业务重点在于表单的增删改查,自然是从form表单开始捣鼓。

正文

antd-form的初始感觉

第一次看antd-form文档时,只是感觉头皮发麻,因为用vue写同样的东西,几行就可以搞定,用react确实要几十行,就觉得,用react做东西太费劲了。不过既然已经准备好跨越React这道坎,闲话不多说,学习就完了。

React相关知识概要

因为本章节重点是讨论ant-form的工作流程,所以相关基础的东西,只做简单概括。

React本身:

React本质上来说,就是一个前端的状态机,state的更新带动了view的更新。

高阶组件

简单理解下,高阶函数通常指一个函数:接受一个函数或者返回一个函数,的函数。
这个概括到高阶组件就是接受一个组件作为参数,返回一个组件的,的函数。嗯。它是一个函数

antd-form

antd-form本身提供一些简单的组件,例如Form,FormItem,并且提供一些简单的功能,Form组件与FormItem组件相互配合,可以直接拿来写表单的业务逻辑。支持错误提示文本与校验状态,但是大部分的内容需要手动去控制。比如触发错误信息的展示,校验状态的改变,等等。

这种情况是基本满足业务需求的,但是,对于写惯了vue的我来说,对于双向数据绑定有种天生的痴迷,此时不得不去探究该控件更高级的用法,自动验证&反馈

因为antd-form中引用了一个叫做rc-form的组件,并且API中与rc-form有大量的重复,所以如果想了解它的自动验证&反馈,就不得不去学习rc-form。

rc-form

初体验

一个最基本的用法


import React from 'react';
import ReactDOM from 'react-dom';
import { createForm, formShape } from 'rc-form';

class Form extends React.Component {
  static propTypes = {
    form: formShape,
  };

  componentWillMount() {
    this.nameDecorator = this.props.form.getFieldDecorator('name', {
      initialValue: '',
      rules: [{
        required: true,
        message: 'What\'s your name?',
      }],
    });
  }

  onSubmit = (e) => {
    e.preventDefault();
    this.props.form.validateFields((error, values) => {
      if (!error) {
        console.log('ok', values);
      } else {
        console.log('error', error, values);
      }
    });
  };

  onChange = (e) => {
    console.log(e.target.value);
  }

  render() {
    const { getFieldError } = this.props.form;

    return (
      <form onSubmit={this.onSubmit}>
        {this.nameDecorator(
          <input
            onChange={this.onChange}
          />
        )}
        <div style={{ color: 'red' }}>
          {(getFieldError('name') || []).join(', ')}
        </div>
        <button>Submit</button>
      </form>
    );
  }
}

const WrappedForm = createForm()(Form);
ReactDOM.render(<WrappedForm />, document.getElementById('__react-content'));

体验链接:点我

效果如图:

clipboard.png

这个案例是非空校验,初始状态无错误提示,当文本框输入内容再清空时候,显示错误内容;或者初始状态直接提交时也会显示错误内容。

流程剖析

代码最底部创建了一个form,通过语句createForm(configObj)(wrappedComp)。组件在componentWillMount时候,通过props.form获得被包装上的方法getFieldDecorator,这个方法返回一个函数,该函数接受一个待修饰的组件,本例子中是input。render部分会调用该函数,该函数内部最终会生成一个inputProps,并在最后通过React.cloneElement(input, {...inputProps})返回被包装的元素。其中inputProps包含一个内部处理过的onChange事件和一个value属性用来显示文本框的值,作用于该被包装元素input上,当input输入值时候,则会调用内部的onChange事件,触发getFieldDecorator中配置的校验规则,校验输入,并在更新结果后,通过this.forceUpdate方法更新组件,重新触发render,走render内部的流程,这样一来,就形成了所谓的双向数据绑定

后续

至此已整理出最基本的demo,但是这只是考虑了最基本的demo。实践中,要考虑到表单的编辑,需要在createForm(configObj)(cmp)中处理configObj,设置mapPropsToFields将后台的数据反显到前端页面中去,并且在字段更新后change回props的来源。当然这就涉及到更麻烦的处理,需要根据业务逻辑酌情处理。

未完待续

不得不说为了考虑框架的拓展性以及严谨,有很多代码看起来很费力,但是值得细挖。挖个坑,后续慢慢填吧。

探究过程中的一些额外收获

npm包

import hoistStatics from 'hoist-non-react-statics';
// 作用:常用语高阶组件中,将被包裹元素的静态方法,“同步”到容器元素中。
hoistStatics(Container, WrappedComponent);
import omit from 'omit.js';
// 作用: 从已经存在的对象中过滤特定属性
const formProps = omit(this.props, [
  'prefixCls',
  'className',
  'layout',
  'form',
  'hideRequiredMark',
]);

A_大白
1k 声望68 粉丝

Alibaba-高德,信息前端持续招人中,欢迎私信我,或者钉钉pws019。