1.0.11 • Published 6 years ago

osci-react-form v1.0.11

Weekly downloads
1
License
MIT
Repository
-
Last release
6 years ago

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 and onFailure.
  • onSubmit is supposed to return a promise. If you cannot do so in certain cases like bcrypt, you have to implement this.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 git@github.com: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

1.0.11

6 years ago

1.0.10

6 years ago

1.0.9

6 years ago

1.0.8

6 years ago

1.0.7

6 years ago

1.0.6

6 years ago

1.0.5

6 years ago

1.0.4

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago