В первой части (Валидация форм React. Часть 1) я описал, как можно работать с react-validate-form, теперь буду улучшать код. Вынесем в отдельный блок поле инпут, подсказки и ошибки. И подключим redux.

import React, {Component} from 'react';
import {connect as vBooConnect} from 'react-validation-boo';
import {connect as reduxConnect} from 'react-redux';
import {InputBlock, InputCheckboxBlock, InputRadioGroupBlock, TextareaBlock, SelectBlock} from '../form/default';

class MyForm extends Component {
    constructor() {
        super();

        this.genderOptions = [
            {value: '', label: 'Ваш пол?'},
            {value: 1, label: 'Мужской'},
            {value: 2, label: 'Женский'}
        ];

        this.familyRadioList = [
            {value: 1, label: 'холост'},
            {value: 2, label: 'сожительство'},
            {value: 3, label: 'брак'}
        ];
    }
    componentWillMount() {
        this.props.vBoo.subscribe('change:input', this.props.onChangeVBooInput);
        this.props.vBoo.subscribe('valid:form', this.props.onChangeVBooValid);
    }
    render() {
        let s = this.props.myStore.inputs;
        return <Form connect={this.props.vBoo.connect}>
            <InputBlock name="name" value={s.name} />
            <InputBlock name="email" value={s.email} />
            <SelectBlock name="gender" options={this.genderOptions} value={s.gender} />
            <InputRadioGroupBlock name="familyStatus" items={this.familyRadioList} value={s.familyStatus} />
            <TextareaBlock name="comment" value={s.comment} />
            <InputCheckboxBlock name="addition" value="yes" checked={s.addition==='yes'} />

            <button onClick={this.sendForm}>
                {this.props.vBoo.isValid() ? 'Можно отправлять': 'Будьте внимательны!!!'}
            </button>
        </Form>
    }
}

export default reduxConnect(
    store => ({
        myStore: { // для наглядности
            isValid: false,
            inputs: {
                email: 'test@mail.ru',
                gender: 0,
                familyStatus: 1
            }
        }
    }),
    dispatch => ({
        onChangeVBooInput: (input) => {...},
	onChangeVBooValid: (isValid) => {...}
    })
)(vBooConnect({
    rules: () => ([...]),
    labels: () => ({...}),
})(MyForm));

Если у вас библиотека не установлена, то её можно скачать git@github.com:tramak/react-validation-boo.git

А если вы устанавливали через npm install react-validation-boo, то обновите до последней версии, на момент публикации это 2.2.4.

Давайте теперь создадим компоненты InputBlock, InputCheckboxBlock, InputRadioGroupBlock, TextareaBlock, SelectBlock.

Обычно есть несколько дизайнов отображение форм. Создадим папку form в ней default это наш дизайн по умолчанию и в нём будем создавать компоненты.

import React from 'react';
import {Input} from 'react-validation-boo';

class InputBlock extends Input {
    getError = () => {
        return this.props.vBoo.hasError() ? <div className="error">{this.props.vBoo.getError()}</div> : '';
    };
    render() {
        return (
            <div>
                <label>{this.props.vBoo.getLabel()}:</label>
                <input {...this.props} onChange={this.change} onBlur={this.blur} />
                {this.getError()}
            </div>
        );
    }
}
export default InputBlock;

import React from 'react';
import {InputCheckbox} from 'react-validation-boo';

export default class InputCheckboxBlock extends InputCheckbox {
    getError = () => {
        return this.props.vBoo.hasError() ? <div className="error">{this.props.vBoo.getError()}</div> : '';
    };
    render() {
        return (
            <div>
                <label>{this.props.vBoo.getLabel()}:</label>
                <input {...this.props} type="checkbox" onChange={this.change} />
                {this.getError()}
            </div>
        );
    }
}

import React from 'react';
import {Textarea} from 'react-validation-boo';

export default class TextareaBlock extends Textarea {
    getError = () => {
        return this.props.vBoo.hasError() ? <div className="error">{this.props.vBoo.getError()}</div> : '';
    };
    render() {
        return (
            <div>
                <label>{this.props.vBoo.getLabel()}:</label>
                <textarea {...this.props} onChange={this.change} onBlur={this.blur} />
                {this.getError()}
            </div>
        );
    }
}

Напишем блок для Select так чтобы опции можно было передавать не только через теги option, а ещё через массив.

let genderOptions = [
    {value: '',  label: 'Ваш пол?'},
    {value: 1, label: 'Мужской'},
    {value: 2, label: 'Женский'}
];
<SelectBlock name="gender" options={genderOptions} />

<SelectBlock name="gender">
    <option value="">Ваш пол</option>
    <option value="1">Мужской</option>
    <option value="2">Женский</option>
</SelectBlock>

import React from 'react';
import {Select} from '../../../react-validation-boo/react-validation-boo/src/main';

export default class SelectBlock extends Select {
    componentWillMount() {
        this.children = this.props.options ? this.__getOptions(): this.props.children;
    }
    componentWillReceiveProps(nextProps) {
        let value = nextProps.value;
        if(this.props.value !== value) {
            this.props.vBoo.change(value);
        }
    }
    __getOptions() {
        return this.props.options.map((item) => {
            return <option value={item.value} disabled={item.disabled}>{item.label}</option>;
        });
    }
    getError = () => {
        return this.props.vBoo.hasError() ? <div className="error">{this.props.vBoo.getError()}</div> : '';
    };
    render() {
        return (
            <div>
                <label>{this.props.vBoo.getLabel()}:</label>
                <select {...this.props} onChange={this.change}>
                    {this.children}
                </select>
                {this.getError()}
            </div>
        );
    }
}

Осталось реализовать компонент InputRadioGroupBlock, мы будем его реализовывать не на базе существующих, а с нуля. Компонент Form библиотеки react-validation-boo ищет среди своих потомков элементы с полем name и в них через props передаёт объект vBoo в этом объекте и находятся все необходимые методы для работы с валидацией компонента. Для начала напишем как мы будем использовать компонент.

let familyRadioList = [
    {value: 1, label: 'холост'},
    {value: 2, label: 'сожительство'},
    {value: 3, label: 'брак'}
];
<InputRadioGroupBlock name="familyStatus" items={familyRadioList} />

import React, {Component} from 'react';

export default class InputRadioGroupBlock extends Component {
    state = {
        value: ''
    };
    componentWillMount() {
        this.setState({value: this.props.value});
    }
    componentDidMount() {
        this.props.vBoo.mount(this.state.value);
    }
    componentWillUnmount() {
        this.props.vBoo.unMount();
    }
    componentWillReceiveProps(nextProps) {
        let value = nextProps.value;
        if(this.props.value !== nextProps.value) {
            this.props.vBoo.change(value);
            this.setState({value});
        }
    }
    getOptions() {
        return this.props.items.map(item => {
            let checked = (this.state.value||'').toString()===(item.value||'').toString();
            return <div key={item.value}>
                       <input type="radio" name={this.props.name} value={item.value} checked={checked} onChange={this.change} />
                       <label>{item.label}</label>
                   </div>;
        });
    }
    change = (event) => {
        let value = event.target.value;
        this.props.onChange && this.props.onChange(event);
        this.props.vBoo.change(value);
        this.setState({value});
    };
    getError = () => {
        return this.props.vBoo.hasError() ? <div className="error">{this.props.vBoo.getError()}</div> : '';
    };
    render() {
        return (
            <div>
                <div>{this.props.vBoo.getLabel()}:</div>
                {this.getOptions()}
                {this.getError()}
            </div>
        );
    }
}

Всё это описано в документации на github, я её буду дописывать.

Если найдёте ошибки или есть предложения по улучшению пишите на почту буду дорабатывать, улучшать.

В комментариях к предыдущей статье было написано что есть другие хорошие библиотеки, да это так, но я считаю, что это не повод не писать свои решения, дорабатывать и улучшать их, когда есть желание написать.

Комментарии (0)