嗯,可能也是最后一版。。。哈哈~~~只是写着玩
简化版的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
, 写在form
的onSubmit
事件中。
onSubmit={handleSumit}
或者onSubmit={handleSubmit(fn)}
,fn
会在form
提交的时候调用,形参为form
中的数据
fields
,和redux-form
一样。参考上面的例子getValues
获取所有的数据getTouchedValues
获取所有改变过(onChange)的数据