如何编写验证函数

How to compose validation functions

我被要求编写函数来验证具有标题和描述输入的表单的数据

在下面的代码中,我使用 ifs 进行验证,但我的老师要求我将这些 ifs 转换为函数并组合它们。

validate = () => {
    let titleError = '';
    let descriptionError = '';
    const { title, description } = this.state;
    const trimTitle = title.trim();

    if (trimTitle === '') titleError = 'The title cannot be empty';
    if (title.length > 500)
      titleError = 'The title cannot be longer than 500 characters';
    if (description.length > 2000)
      descriptionError =
        'The description cannot be longer than 2000 characters';

    if (titleError !== '' || descriptionError !== '') {
      this.setState({ titleError, descriptionError });
      return false;
    }

    return true;
  };

我试过制作如下功能:

emptyTitle = ({ titleError }) => {
    // let titleError = '';
    const trimTitle = title.trim();
    if (trimTitle === '') titleError = 'The title cannot be empty';
    this.setState({ titleError });
  };

  maxTitleLength = ({ title }) => {
    let titleError = '';
    if (title.length > 500)
      titleError = 'The title cannot be longer than 500 characters';
    this.setState({ titleError });
  };

但我不确定我的方法。拜托,我将非常感谢任何建议。

如果你想应用所有的验证规则并得到所有的错误,你可以规范化输入以便能够累加结果:

/* You can create all validation functions following the pattern:
 * validationFunc(rule, msg)(normalizedParam): ValidationResult
 */
 
 const normalizeParam = param => {
   if (typeof param.errors === 'undefined') {
     return {
       value: param,
       errors: []
     }
   }
   
   return param
 }
 
 const makeError = function (param, msg) {
   return {
     ...param,
     errors: param.errors.concat(msg)
   }
 }
 
 const makeRule = (msg, rule) => param => {
   const normalizedParam = normalizeParam(param)
   
   if (rule(normalizedParam.value)) {
      return normalizedParam
   } else {
      return makeError(normalizedParam, msg)
   }
 }
 
 const validationGreaterThan = (limit, msg) => makeRule(msg, function(param) {
    return param > limit
 })
 
 const validationLesserThan = (limit, msg) => makeRule(msg, function(param) {
    return param < limit
 })
 
 const validate = R.compose(
    validationGreaterThan(10, 'The number must be greater than 10'),
    validationLesserThan(100, 'The number must be lesser than 100')
 )
 
 let result = validate(1)

 if (result.errors.length) {
   result.errors.forEach(err => console.log(err))
 } else {
   console.log('Success!')
 }
 
 result = validate(101)
 
  if (result.errors.length) {
   result.errors.forEach(err => console.log(err))
 } else {
   console.log('Success!')
 }
 
  result = validate(20)
 
  if (result.errors.length) {
   result.errors.forEach(err => console.log(err))
  } else {
   console.log('Success!')
  }
 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

但是如果你想削减验证过程,你可以在发现错误时抛出异常(每次验证都会抛出一个错误或者returns参数如果一切正常)。

您可以在此处研究其他方法:

这是使用 ramda cond 方法的一种可能实现:

this.state = {title: '', description: ''};

this.setState = (s) => { console.log(s); };

isEmpty = (t) => (t === '');

isLong = (t, n) => (t.length > n);

emptyTitle = (t) => isEmpty(t.trim());

longTitle = (t) => isLong(t, 500);

longDesc = (d) => isLong(d, 2000);

titleValidator = R.cond([
  [emptyTitle, R.always('The title cannot be empty')],
  [longTitle, R.always('The title cannot be longer than 500 characters')],
  [R.T, R.always('')]
]);

descValidator = R.cond([
  [longDesc, R.always('The description cannot be longer than 2000 characters')],
  [R.T, R.always('')]
]);

thereIsError = (e) => (e.title !== '' || e.description !== '');

errorDetected = function (e) {
  this.setState(e); 
  return false;
};

validate = () => {
    const { title, description } = this.state;

    const validationError = {
      title: titleValidator(title), 
      description: descValidator(description)
    };
    
    const errorLog = R.cond([
      [thereIsError, errorDetected],
      [R.T, R.T]
    ]); 
    
    return errorLog(validationError);
};

console.log('Validate:', validate.call(this));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

我想你被要求避免命令式控制流,R.cond 是封装 if/else 逻辑的功能方式。希望对你有帮助。

const createValidator = (name, { min, max }) => R.cond([
  [
    // normalize and compare min-length
    R.pipe(R.trim, R.length, R.gte(min)), 
    // eventually return error message
    R.always(`${name} cannot be empty`)
  ],
  
  [
    // normalize and compare max-length
    R.pipe(R.trim, R.length, R.lt(max)), 
    // eventually return error message
    R.always(`${name} too long`)
  ],
  
  // all good, no errors to display
  [R.T, R.always(null)]
]);


// The following can be ignored, 
// only needed as boilerplate to prove the example
const Test = () => {
  const [title, setTitle] = React.useState('');
  const [description, setDescription] = React.useState('');
  const withValue = (cb) => R.pipe(R.pathOr('', ['target', 'value']), cb);
  
  const isTitleValid = createValidator('Title', { min: 0, max: 500 });
  const isDescriptionValid = createValidator('Description', { min: 0, max: 2000 });
  
  
  return (
    <form onSubmit={e => e.preventDefault()}>
      <div className="form-group">
        <input className="form-control" placeholder="Title" value={title} onChange={withValue(setTitle)} />
      </div>

      <div className="form-group">
        <textarea className="form-control" placeholder="Description"  onChange={withValue(setDescription)} value={description} />
      </div>
      
      <ul className="text-danger">
        <li>{isTitleValid(title)}</li>
        <li>{isDescriptionValid(description)}</li>
      </ul>
    </form>
  );
};

ReactDOM.render(<Test />, document.querySelector('#test'));
form { margin: 1em 2em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js" integrity="sha256-xB25ljGZ7K2VXnq087unEnoVhvTosWWtqXB4tAtZmHU=" crossorigin="anonymous"></script>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">

<div id=test></div>