嗯,可能也是最后一版。。。哈哈~~~只是写着玩

简化版的redux-form,只是觉得不需要redux-form那么复杂的功能,也不想要和redux关联,而且希望有一个简单管理form的东西,所以就写了一个。肯定有很多不足,比如checkbox/radio group怎么管理。。。没有解决。。。

import React from 'react';

export default function reactForm(options){
  const { fields=[], initialValues={}, validate, validateOnBlur, withRef } = options;

  return (Component)=>{
    class Form extends React.Component {
      constructor(props) {
        super(props);

        this.initialValues = { ...initialValues, ...props.initialValues };
        this.state = this.getInitialFields();

        this.touchedKeys = {};
      }
      
      componentWillReceiveProps(nextProps){
          if(this.props.initialValues != nextProps.initialValues) {
              this.initialValues = { ...initialValues, ...nextProps.initialValues };
              this.resetForm();
          }
      }

      getInitialFields = ()=>{
        return fields.reduce((prev, key)=>{
          prev[key] = typeof this.initialValues[key] == "undefined" ? undefined : this.initialValues[key];
          return prev;
        }, {})
      }

      resetForm = ()=>{
        this.setState(this.getInitialFields());
      }

      setInstance = (instance)=>{
        this.instance = instance;
      }

      getInstance = ()=>{
        if(withRef) return this.instance;
        console.error("Can not get instance when withRef is false");
      }

      getValues = ()=>{
        return fields.reduce((prev, key)=>{
          prev[key] = this.state[key];
          return prev;
        }, {});
      }

      getTouchedValues = ()=>{
        let result = {};
        for(let key in this.touchedKeys) {
          if(this.touchedKeys.hasOwnProperty(key)){
            result[key] = this.state[key];
          }
        }
        return result;
      }

      onFieldChange = (e, key)=>{
        let value = ['radio', 'checkbox'].includes(e.target.type) ? e.target.checked : e.target.value;

        console.log(`trigger field change with ${key} ${value}`);

        this.setState({
          [key]: value
        }, ()=>{
          this.touchedKeys[key] = true;
        });
        validate && validate(key, value);
      }

      onFieldBlur = (e, key)=>{
        let value = ['radio', 'checkbox'].includes(e.target.type) ? e.target.checked : e.target.value;

        validateOnBlur(key, value);
      }

      handleSubmit = (fn)=>{
        if(typeof fn == "function") {
          return (e)=>{
            e.preventDefault();
            e.stopPropagation();
            fn(this.getValues());
          }
        } else {
          fn.preventDefault();
          fn.stopPropagation();
        }
      }

      buildFields = ()=>{
        return fields.reduce((prev, key)=>{
          let value = this.state[key];
          let field = { onChange: (e)=>{ this.onFieldChange(e, key) } };
          
          if(typeof value === "boolean") field.checked = value;
          else field.value = value;

          if(validateOnBlur) field.onBlur = (e)=>{ this.onFieldBlur(e, key) };

          prev[key] = field;
          return prev;
        }, {})
      }

      buildProps = (props)=>{
        let _props = { ...props };

        _props.fields = this.buildFields();
        _props.handleSubmit = this.handleSubmit;
        _props.getValues = this.getValues;
        _props.getTouchedValues = this.getTouchedValues;
        _props.resetForm = this.resetForm;

        if(withRef) {
          _props.ref = this.setInstance;
        }

        return _props;
      }

      render(){
        let props = this.buildProps(this.props);

        return <Component { ...props } />;
      }
    }

    return Form;
  }
} 

用例:

index.js

import React from 'react';
import Form from './form';

export default class FormApp extends React.Component {
  constructor(props) {
    super(props);
  }

  onClick = ()=>{
    console.log(this.instance.getTouchedValues());
  }

  render(){
    return (
      <div>
        <Form ref={instance=>{ this.instance=instance; }} initialValues={{ name: true }}/>
        <button style={{ marginLeft: "200px" }} onClick={this.onClick}>Values</button>
      </div>
    )
  }
}

form.js

import React from 'react';
import reactForm from 'components/react-form';

function validate(key, value){
  console.log(`validateOnBlur ${key} ${value}`);
}

@reactForm({ fields: ['name', 'bbb'], withRef: true, initialValues: { bbb: "bbbbbb" }, validateOnBlur: validate })
export default class Form extends React.Component {
  constructor(props) {
    super(props);
  }

  onSubmit = (values)=>{
    console.log(values);

    let { getTouchedValues } = this.props;
    console.log(getTouchedValues());

    this.props.resetForm();
  }

  render(){
    let { fields: { name, bbb }, handleSubmit } = this.props;

    return (
      <form onSubmit={handleSubmit(this.onSubmit)} style={{ marginLeft: "200px" }}>
        <label>
          name:
          <input type="checkbox" name="name" value="123" {...name}/>
        </label>
        <label>
          bbb
          <input type="text" name="bbb" {...bbb}/>
        </label>
        <button type="submit">submit</button>
      </form>
    )
  }
}

用法:

@reactForm(options)

options: {
  fields: ["field1", "field2", "field3", "checkbox"...], // names of fields
  initialValues: { "field1":"value1", "field2":"value2", "checkbox":true }, // the initial values of fields
  validate: fn, // the function will be called when onChange
  validateOnBlur: fn, // the function will be called when onBlur
  withRef: false // when this is true, you can get the inner component by using getInstance
}

Component 接受一个initialValues参数。和上面的initialValues作用一样,相同的key会覆盖option.initialValues中的值

API

  • handleSubmit, 写在formonSubmit事件中。

onSubmit={handleSumit}或者onSubmit={handleSubmit(fn)}fn会在form提交的时候调用,形参为form中的数据

  • fields,和redux-form一样。参考上面的例子

  • getValues 获取所有的数据

  • getTouchedValues 获取所有改变过(onChange)的数据


Clark
3.4k 声望82 粉丝

[链接]