osci-react-form
v1.0.11
Published
* write JSX in the react containment style * automatic dependency injection on values and event handlers * instictive validation interfaces
Downloads
5
Readme
key features
- write JSX in the react containment style
- automatic dependency injection on values and event handlers
- instictive validation interfaces
npm
npm install --save-dev osci-react-form
a basic example
import React, { Component } from "react";
import Form, { enhance, Input } from "osci-react-form";
export default class SampleForm extends Component {
constructor(props) {
super(props);
this.state = {
values: {},
config: {
asyncValidateOnBlur: true,
asyncValidateOnInit: true,
touchNonEmptyOnInit: true,
fieldsToNormalize: [],
normailzeTest: /^$/
}
};
}
render() {
return (
<Form scope={this} onSubmit={this.handleSubmit}>
<Input name="username" />
<Input name="password" type="password" />
<button type="submit">Sign in</button>
<button type="reset">reset</button>
</Form>
);
}
}
scope attribute
The attribute
scope
is the client that has the state of Form.If it is not defined, it will be the instance of Form itself.
state control methods
You can execute
enhance(scope)
to enable the state control methods.This method should be executed before any execution of the methods.
scope can be either an instance or a class. (not plain functions. React plain functions cannot have
this
.)It is trivial that the scope attribute and the scope argument should be the same.
fields definition
- Form regards components with names as fields.
method caveats
If a method modifies this.state
or reads it not in the 'render' method frame, put the previous state as its argument,
this.setState(prev => {
this.setValue("username", "pororo", prev);
return true;
});
else just use the mothod directly.
this.isValid("username");
If you implemented React.Component abstract methods like getSnapShotBeforeUpdate
, you have to set the state in the immutable way. (frankly speaking...i have not tested this case yet)
this.setState(prev => {
this.setValue("username", "pororo", prev);
return { values: this.getCopy(prev.values) };
});
define validators
- the custom validator interface
validator : String || { name : String, regex : RegExp || api : function, async : boolean [Optional], message : String [Optional] }
- client definition
<input validators={[ validator1, validator2, ...]} ... />
- built-in definition
<input name="confirm" match="password" ... />
the list of the built-in validators
- type="email"
- required
- match="anyNameOfField"
- notMatch="anyNameOfField"
- pattern={/.*/}
- assertTrue={anyCustomValidator}
- assertFalse={anyCustomValidator}
ordering built-in validators with strings
<Input validators={[ 'required', validator1, 'email', 'pattern', validator2, 'match', 'assertFalse', ...]} pattern={/(1|2)/} match="password" ...built-in-validators />
- validation scoping
You can avoid each component having the same validator objects and their value caches for the same validation functionality.
<ul>
{arr.map((v, i) =>
<Input key={i.toString()}
validators={...validators}
validationScope="serialNumber" />
)}
</ul>
<Input validators={...validators} validationScope="serialNumber" />
validator caveats
- Once validators are registered, you cannot change them.
api
mothods should return a promise that returns a boolean.- The
required
built-in validator always goes first.
methods that reads this.state
'getValue(name : String, prev : Object [Optional]) : String'
'isTouched(name : String, prev : Object [Optional]) : Boolean'
'syncValidate(name : String, prev : Object [Optional], value : Object [Optional]) : String || null'
'syncValidateFull(name : String, prev : Object [Optional], value : Object [Optional]) : Array'
'isSyncValid(name : String, prev : Object [Optional]) : Boolean'
'getSyncValidator(name : String, prev : Object [Optional]) : Array'
- no Validator class yet
'getAsyncStatus(name : String, prev : Object [Optional]) : Object { [asyncName1] : 'processing' || 'resolved' || 'rejected', [asyncName2] : ... } || null'
'getFieldNames(prev : Object [Optional]) : Array'
'isAsyncValidating(name : String [Optional], prev : Object [Optional]) : Boolean'
'isAsyncRejected(name : String [Optional], prev : Object [Optional]) : Boolean'
'getAsyncErrors(name : String, prev : Object [Optional]) : Object { [asyncName1] : 'message or name', [asyncName2] : ... } || null'
'getAsyncIdleFields(name : String, prev : Object [Optional]) : Array'
'getProcessingPromises(prev : Object [Optional]) : Array'
methods that modifies this.state
'setValue(name : String, value : any, prev : Object) : Boolean'
'deleteValue(name : String, prev : Object) : true'
'touch(name : String, prev : Object) : Boolean'
'touchAll(name : String, prev : Object) : Boolean'
'untouch(name : String, prev : Object) : true'
'asyncValidate(name : String, prev : Object, value : Object [Optional]) : Promise<{asyncName : String, message : String, value : Object, result : Boolean}>'
'normalize(prev : Object, names : Array [Optional]) : undefined'
cleans up empty strings for the names in the second argument or field names defined in the configuration of 'fieldsToNormalize'
this method is for dynamic fields that are not assigned any values to.
plain methods
'getCopy(original : Object) : Object'
- deeply copy all properties
submit interface
<Form
onSubmit={this.onSubmit}
onSuccess={this.onSuccess}
onFailure={this.onFailure}
/>
- Form attributes could have
onSubmit
,onSuccess
andonFailure
. onSubmit
is supposed to return a promise. If you cannot do so in certain cases like bcrypt, you have to implementthis.setState({submitting : false})
manually.- The inner source code of submit handling is below
submit() {
const { scope } = this;
const body = scope.getCopy(scope.state.values);
const submitAction = this.props.onSubmit(body);
if (submitAction instanceof Promise) {
submitAction
.then(
(!scope.isUnmounted && this.props.onSuccess) || (result => result),
(!scope.isUnmounted && this.props.onFailure) || (result => result),
)
.then(this.handleAfterSubmitCompletion);
}
}
control the unmounted component
- You can put the code below to avoid the unmounted component error with setState.
if (scope.isUnmounted) {
// scope.setState({ any });
}
the sample page
http://osci-react-form.surge.sh/
test the sample code
git clone [email protected]:OpenSourceConsulting/osci-react-form.git
cd osci-react-form
npm install
npm start
update logs
- 2018/5/15 update logs have been refreshed.
issues
eager
- test React.Component abstract methods with the state control methods
- asyncValidateOnChange, asyncValidateOnChangeForNonText, asyncValidateOnBlurForNonText, suppressAutoDenpency
lazy
- uncontrolled component 디자인 구상, ref? no? ex) type="file"
- html5 의 모든 스펙을 구현해야한다. (range, textarea, contenteditable, submit action async ...)
- old browser, study of xss cases
- old browser, onSubmit setState promise event loop hole 학습 및 테스트, 최신브라우져는 테스트 완료
- customizing 혹은 logging 을 위해 다양한 hook 을 제공해야한다. promise 의 대부분 stack 에서 logging 할 수 있도록 할 것이다.
extra
- componentDidCatch 사용성 테스트, 좋은 인터페이스인가
- dev logging, dev minifying 구현
- chrome dev tool perfomance test
LICENSE
The MIT License (MIT)
Copyright (c) 2018 OSCI