技术控

    今日:124| 主题:49179
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] Elegant Form Validation Using React

[复制链接]
下一秒落寞 发表于 2016-10-6 10:28:17
120 2

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
Over the past few months, I’ve really been enjoying learning to use    Reactfor front-end web development. I love how React encourages you to write clean code by breaking your presentation components into small chunks that are easy to reuse.  
  Lately, I’ve been working on an approach to add form validation to React components, and I’d like to share what I came up with.  
  A Sample Problem

  My goal was to hide all errors initially, waiting until the user presses “submit” to present any errors. Once the errors were visible, I wanted them to go live and automatically clear as the user changes the input data.
      
Elegant Form Validation Using React-1 (definition,breaking,learning,waiting,example)
  
  Declaring validation rules

  In this example, I’m adding validation to the text fields on a Create Account screen. Looking at the CreateAccount.jsx component below, the first thing you’ll see is the definition of    fieldValidations. This is where I define the set of validations that should be performed on each field in the form.  
  I did this by creating a list of    ruleRunnerswhich involves passing a key, a name, and a list of rules. The key is used to identify which value in the component’s state the rule should be run on. The name is a friendly name that will be used to construct an error message when validation fails.  
      The following line sets up validation for the password field.
        [code]ruleRunner("password1", "Password", required, minLength(6)),[/code]  This means that    this.state.password1is required to have a value, and that value's minimum length is six characters. If validation fails, the field should be referred to as "Password" in the resulting error message.  
  handleFieldChanged()

  The next important piece of the CreateAccount component is the    handleFieldChanged()function. This is the function that is to be called whenever the text in any of the form fields changes. It starts by creating a copy of this.state and updating it with the value that just changed. It then calls run(), passing in the new state and the list of validation ruleRunners that we declared above.  
  The run() function performs the validation checks and returns a key-value pair or validation errors. If the value that changed was the password, and it was only five characters, the result would look something like this:
  [code]{"password1": "Password must be at least 6 characters"}.[/code]  The result of run() is assigned to newState.validationErrors.
  Set the component state

  Finally, we call    this.setState(newState)to commit the change and the updated validation results in our component state.  
  When the user submits the form data, the validation is already complete. All we have to do is check if    validationErrorsis an empty object or not. If it's empty, we can go ahead and perform whatever we need to do to submit the data.  
  Making errors live

  One other thing I'd like to point out is the showErrors flag that exists in the component state. This flag is passed to each of the fields that are rendered as an indicator of whether they should show their corresponding error or not. Initially, all the fields will likely have errors on them because they are empty.
  However, since the user hasn't attempted to submit yet, we don't want to show these errors. The    showErrorsflag is defaulted to false, but set to true once the the user clicks submit. From that point on, all errors are visible and updated live as the user changes the form data.  
  [code]// CreateAccount.jsx

const fieldValidations = [
  ruleRunner("name", "Name", required),
  ruleRunner("emailAddress", "Email Address", required),
  ruleRunner("password1", "Password", required, minLength(6)),
  ruleRunner("password2", "Password Confirmation", mustMatch("password1", "Password"))
];

export default class CreateAccount extends React.Component {
  constructor() {
    // ...
    this.state = {
      showErrors: false,
      validationErrors: {}
    };

    // Run validations on initial state
    this.state.validationErrors = run(this.state, fieldValidations);
  }
  
  handleFieldChanged(field) {
    return (e) => {
      // update() is provided by React Immutability Helpers
      // https://facebook.github.io/react/docs/update.html
      let newState = update(this.state, {
        [field]: {$set: e.target.value}
      });
      newState.validationErrors = run(newState, fieldValidations);
      this.setState(newState);
    };
   
  handleSubmitClicked() {
    this.setState({showErrors: true});
    if($.isEmptyObject(this.state.validationErrors) == false) return null;
    // ... continue submitting data to server
  }

  render() {
    return (
      

                           text={this.props.emailAddress} onFieldChanged={this.handleFieldChanged("emailAddress")}
                  errorText={this.errorFor("emailAddress")} />
        
        // Render Name, Password, Submit button, etc. fields
      

    );
  }
}[/code]  ruleRunner

  Next, let's look at the ruleRunner function. ruleRunner is a thunk, or a function that returns a function. We call it to construct a runner function that takes the updated state and runs the specified validations against said state.
  We haven't looked at the implementation of the validation rules yet. For now, you just need to know that they are also thunks. They must return either null (if there was no error) or a function that will be used to construct an error message. That function takes as a parameter the friendly name of the field that was passed in to the original call to ruleRunner() so that a meaningful error message can be made. If a validation check returns a non-null value, the ruleRunner calls the resulting function and then returns a key-value pair of the field key and the error message.
  My implementation only allows one error per field to be reported. It would be very simple to tweak the implementation to return an array of error messages instead to support displaying multiple validation errors per field.
  run()

  The other function that I have defined next to    ruleRunner()is    run(). This is a very simple function that just calls all the validation rule runners and aggregates their results into a single object. It allows us to have the results of all the fields in one object.  
  
  [code]// ruleRunner.js

export const ruleRunner = (field, name, ...validations) => {
  return (state) => {
    for (let v  of validations) {
      let errorMessageFunc = v(state[field], state);
      if (errorMessageFunc) {
        return {[field]: errorMessageFunc(name)};
      }
    }
    return null;
  };
};

export const run = (state, runners) => {
  return runners.reduce((memo, runner) => {
    return Object.assign(memo, runner(state));
  }, {});
};[/code]  Implementing rules

  Moving one level deeper, the next thing to look at is the implementation of the rules. The rules come in two forms. The simplest is just a function that looks at the value being passed in and returns null or an error message constructor. This is how the    requiredrule works.  
  The second form is a bit more complicated because it actually constructs a new function that does the validation check. This allows the rules to be reused on various fields with slightly different requirements. For example, the minLength function constructs a rule that checks for a specific length. It could be used somewhere else to check for a different length.
  You can see the two different forms of rules when looking at the declaration of field validations that we discussed earlier. Notice that the required rule function is not called in the declaration, but only used as a reference to that function. In contrast, the minLength function is called with a value of six during the declaration. If you find that confusing, you could use a naming convention to help distinguish between the two types. For example, you might name them: requiredRule and minLengthRuleBuilder.
  
  [code]// rules.js
import * as ErrorMessages from './errorMessages.js';

export const required = (text) => {
  if (text) {
    return null;
  } else {
    return ErrorMessages.isRequired;
  }
};

export const mustMatch = (field, fieldName) => {
  return (text, state) => {
    return state[field] == text ? null : ErrorMessages.mustMatch(fieldName);
  };
};

export const minLength = (length) => {
  return (text) => {
    console.log("Checking for length greater than ", length);
    return text.length >= length ? null : ErrorMessages.minLength(length);
  };
};[/code]  Building error messages

  When a rule determines that a value is invalid, it returns a function that is used to build an appropriate error message. I like to have all my error messages in one place that is easy to find, so I separate them into an errorMessages file. This also makes them easy to reuse if necessary. Alternatively, you could put the implementation of the error message builders right in the rules.
  
  [code]// errorMessages.js

export const isRequired = fieldName => `${fieldName} is required`;

export const mustMatch = otherFieldName => {
  return (fieldName) => `${fieldName} must match ${otherFieldName}`;
};

export const minLength = length => {
  return (fieldName) => `${fieldName} must be at least ${length} characters`;
};[/code]  That's it for the construction and processing of the validation rules.
  TextField

  The last thing I want to show is my TextField component. As you can see, this component is completely stateless. It displays the value that was passed in. When the user changes the value, it calls the onFieldChanged callback that was also passed in as props. For the error message functionality, it looks at two other props,    showErrorand    errorText. If    showErroris true and errorMessage contains a value, the error message is rendered.  
  
  [code]// TextField.jsx

import React from 'react';
import OptionallyDisplayed from './OptionallyDisplayed.jsx';

export default class TextField extends React.Component {

  constructor(props) {
    super(props);
    this.shouldDisplayError = this.shouldDisplayError.bind(this);
  }

  shouldDisplayError() {
    return this.props.showError && this.props.errorText != "";
  }

  render() {
    return (
      

                        value={this.props.text} onChange={this.props.onFieldChanged}  />
        
         

            {this.props.errorText}
         

        

      

    );
  }
}

TextField.propTypes = {
  showError: React.PropTypes.bool.isRequired,
  onFieldChanged: React.PropTypes.func.isRequired
};[/code]  So that is my approach to form validation of React components. I like it because adding validation to a new form requires only a few lines of code, and the rules are specified in a very declarative way. I also like how the implementation is split up into small manageable pieces, each serving a very specific purpose.
  I want to give credit to my coworker Chris Farber, who worked with me to come up with the original implementation of this approach.
友荐云推荐




上一篇:Building Your Own React Clone in Five Easy Steps
下一篇:The Rise and Fall of Scala
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

白萱 发表于 2016-10-12 15:20:09
2016-10-12是什么日子?
回复 支持 反对

使用道具 举报

huihaitong 发表于 2016-11-14 09:20:38
顶贴不认真,大脑有问题。
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表