Пару лет назад я увидел на Хабре статью про LIVR и с тех пор использую библиотеку на всех проектах. С переходом на React я адаптировал для валидации ее же, т.к. существующие решения не предлагали гибкости которой мне хотелось. Свое решение я уже использую на двух проектах и решил выложить в npm — может кому-то еще оно покажетсяя удобным. 
Пакет называется react-livr-validation.
Пример базового использования:
Компонент принимает валидационную схему и первоначальные данные(если данные не валидны, кнопка submit сразу будет неактивна), так же можно передать custom rules и aliased rules:
Обертка ValidationInput добавляет в инпут свои обработчики событий, на которые будет происходить валидация. По умолчанию это события change, blur, keyup.
Есть возможность реализовать свою обертку — пакет экпортит HOC, который прокидывает в пропсы api
              
            Пакет называется react-livr-validation.
Пример базового использования:
import React from 'react';
import Validation, {DisabledOnErrors, ValidationInput} from 'react-livr-validation';
const schema = {
    login: ['required', 'not_empty'],
    password: ['required', 'not_empty']
};
const data = {
    login: '',
    password: ''
};
export default function() {
    return (
        <Validation
            data={data}
            schema={schema}
        >
            <form>
                <ValidationInput name="login" >
                    <input name="login" />
                </ValidationInput>
                <ValidationInput name="password" >
                    <input name="password" type="password" />
                </ValidationInput>
                <DisabledOnErrors>
                    <input type="submit" />
                </DisabledOnErrors>
            </form>
        </Validation>
    );   
}
Компонент принимает валидационную схему и первоначальные данные(если данные не валидны, кнопка submit сразу будет неактивна), так же можно передать custom rules и aliased rules:
const customRules = {
    alpha_chars: function() {
        return function(value) {
            if (typeof value === 'string') {
                if (!/[a-z,A-Z]+/.test(value)) {
                    return 'WRONG_FORMAT';
                }
            }
        };
    }
};
const aliasedRules = [
    {
        name: 'strong_password',
        rules: { min_length: 6 },
        error: 'TOO_SHORT'
    }
];
<Validation
      data={data}
      schema={schema}
      rules={customRules}
      aliasedRules={aliasedRules}
>
      // ... form
</Validation>
Обертка ValidationInput добавляет в инпут свои обработчики событий, на которые будет происходить валидация. По умолчанию это события change, blur, keyup.
<ValidationInput name="password" valicateOnEvents={['change', 'blur', 'keyUp']}  >
        <input name="password" type="password" />
</ValidationInput>
 Есть возможность реализовать свою обертку — пакет экпортит HOC, который прокидывает в пропсы api
// @flow
import React, {Component} from 'react'
import {ValidationComponent} from 'react-livr-validation'
import get from 'lodash/get'
import noop from 'lodash/noop'
import compose from 'ramda/src/compose'
import styled from 'styled-components'
type DataChunk = {
    name: string,
    value: any
}
type State = {
    touched: boolean
}
type Props = {
    // will be passed by HOC
    setData: (data: DataChunk) => void,
    getError: (name: string) => ?string,
    getErrors: () => Object,
    className: string, // for the error block
    style: Object // for the error block
    errorCodes: Object,
    
    name: string,
    field: string
}
class NestedError extends Component {
    props: Props;
    
    isTouched() {
        const {children} = this.props;
        return get(children, 'props.value')
    }
    
    state: State = {
        touched: this.isTouched()
    }
    
    setTouched() {
        this.setState({
            touched: true
        })
    }
    
    cloneElement() {
        const {children} = this.props;
        const onBlur = get(children, 'props.onBlur', noop);
        return React.cloneElement(
            children,
            {
                onBlur: compose(this.setTouched, onBlur)
            }
        )
    }
    
    render() {
        const {touched} = this.state;
        const {
            children, 
            field, 
            name, 
            getError,
            errorCodes,
            style,
            className
        } = this.props;
        const errors = getErrors();
        const error = get(errors, `${field}`.${name});
        return (
            <div>
                {touched ? children : this.cloneElement()}
                {error &&
                    <Error
                        className={className}
                        style={style}
                    >
                        {errorCodes[error] || error}
                    </Error>
                }
            </div>
        );
    }
}
const Error = styled.div`
    color: red;
`;
export default ValidationComponent(NestedError)
			  Комментарии (7)

turbo_exe
10.09.2017 22:30ваша концепция LIVIR очень похожа на то, что я хотел от валидации на JS с год назад. я ничего стоящего не нашёл тогда и в итоге написал свой велосипед который очень похож на ваш, но работает только на js и обладает (пока) меньшим функционалом.
вопрос: можно ли в LIVIR сделать так чтобы поле валидировалось только при определённых условиях? например, валидировать email только если пользователь подписался на рассылку. в моём велосипеде это делается как-то так:
const { validate, util: { when }, rules: { isBoolean, isEmail } } = require('yeval'); const optedInForNewsletter = (value, data) => data.optedInForNewsletter === true; validate( // declare rules as first argument { optedInForNewsletter: isBoolean, email: when(optedInForNewsletter, isEmail), }, // pass data as second argument { optedInForNewsletter: true } ) .then(errors => { console.log(errors); // { email: 'Must be a valid email address' } });
koorchik
11.09.2017 10:14+1У нас в проектах мы сделали правило required_if
И пишем так:
{ optedInForNewsletter: {one_of: [1, 0]}, email: [{required_if: 'optedInForNewsletter'}, 'email'], }

one_more Автор
11.09.2017 11:31Как вариант можно еще динамически менять правила валидации, если правил много и применять надо только при условии.
import React, {Component} from 'react'; import Validation, {DisabledOnErrors, ValidationInput} from 'react-livr-validation'; const data = { login: '', email: '' }; class App extends Component { state = { withEmail: false }; toggleWithEmail = ({target: {checked}}) => { this.setState({ withEmail: checked }) }; render() { const {withEmail} = this.state; const schema = { login: ['required', 'not_empty'], email: withEmail ? ['required', 'not_empty', 'email'] : [] }; return ( <div> <Validation data={data} schema={schema} > <form> <ValidationInput name="login"> <input name="login"/> </ValidationInput> <ValidationInput name="email"> <input name="email" type="email"/> </ValidationInput> <div> <label htmlFor="subscribe">subscribe</label> <input id="subscribe" type="checkbox" onChange={this.toggleWithEmail} /> </div> <DisabledOnErrors> <input type="submit"/> </DisabledOnErrors> </form> </Validation> </div> ); } }
          
 
argonavtt
Вы
</Errorне закрыли в последнем примереone_more Автор
поправил