Last active
February 28, 2024 01:57
-
-
Save romanlex/bf61fb0c3db2ef1a6c68cb07a0904da2 to your computer and use it in GitHub Desktop.
react-final-form + validation.js + Fragment + Portal + outside submitting [example: https://codesandbox.io/s/j3qry8r38y]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
import Type from 'prop-types'; | |
import { DividerChildContent } from 'ui/components/DividerBlock'; | |
import CallbackForm from './CallbackForm'; | |
class Callback extends Component { | |
render() { | |
const containerActionId = `CallbackFormActions`; | |
return ( | |
<DividerChildContent noMargin className="callback-form__wrapper"> | |
<div className="mt-auto" /> | |
<CallbackForm | |
buttonPortalId={containerActionId} | |
subscription={{ submitting: true, pristine: true }} | |
/> | |
<div className="mt-auto" /> | |
<footer | |
className="divider-block__footer-actions" | |
id={containerActionId} | |
/> | |
</DividerChildContent> | |
); | |
} | |
} | |
Callback.propTypes = {}; | |
Callback.defaultProps = {}; | |
export default Callback; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { PureComponent, Fragment } from 'react'; | |
import ReactDOM from 'react-dom'; | |
import Type from 'prop-types'; | |
import validate from 'validate.js'; | |
import InputMask from 'react-input-mask'; | |
import cx from 'classnames'; | |
import { Form, Field, FormSpy } from 'react-final-form'; | |
import { RegexPhone } from 'ui/lib/phones'; | |
import { FormGroup } from 'reactstrap'; | |
import { autobind } from 'core-decorators'; | |
const constraints = { | |
name: { | |
presence: { | |
message: 'Пожалуйста, укажите корректное имя', | |
}, | |
format: { | |
pattern: '[a-zа-яёЁ0-9]+', | |
flags: 'i', | |
message: 'Имя может содержать только буквы и цифры, знак пробела', | |
}, | |
}, | |
phone: { | |
presence: { | |
message: 'Укажите корректный номер телефона', | |
}, | |
format: { | |
pattern: RegexPhone['ru-RU'], | |
flags: 'i', | |
message: 'Укажите корректный номер телефона', | |
}, | |
}, | |
}; | |
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); | |
class FormButtons extends PureComponent { | |
constructor(props) { | |
super(props); | |
this.el = document.createDocumentFragment(); | |
} | |
componentDidMount() { | |
const { portalSelector } = this.props; | |
document.getElementById(portalSelector).appendChild(this.el); | |
} | |
componentWillUnmount() { | |
const { portalSelector } = this.props; | |
document.getElementById(portalSelector).removeChild(this.el); | |
} | |
render() { | |
const { submit, reset, submitting, pristine } = this.props; | |
return ReactDOM.createPortal( | |
<Fragment> | |
<button | |
className="btn btn-primary btn-block" | |
type="submit" | |
onClick={submit} | |
disabled={submitting} | |
> | |
Заказать | |
</button> | |
<button | |
className="btn btn-primary btn-block" | |
type="button" | |
onClick={reset} | |
disabled={submitting || pristine} | |
> | |
Сбросить | |
</button> | |
</Fragment>, | |
this.el | |
); | |
} | |
}; | |
class CallbackForm extends PureComponent { | |
@autobind | |
async submitHandler(values) { | |
await sleep(300); | |
window.alert(JSON.stringify(values, 0, 2)); | |
} | |
render() { | |
const { subscription, buttonPortalId } = this.props; | |
return ( | |
<Form | |
onSubmit={this.submitHandler} | |
subscription={subscription} | |
validate={values => { | |
console.log(values); | |
const errors = {}; | |
const data = { ...values }; | |
if (data.phone) data.phone = data.phone.replace(/[()_\-\s]/g, ''); | |
const validatedMessages = | |
validate(data, constraints, { fullMessages: false }) || {}; | |
Object.keys(validatedMessages).map(key => { | |
errors[key] = validatedMessages[key].join('<br />') || ''; | |
}); | |
return errors; | |
}} | |
render={({ handleSubmit, reset, submitting, pristine, values }) => ( | |
<form | |
id={`callback-form`} | |
onSubmit={handleSubmit} | |
className="form form--default callback-form" | |
> | |
<Field name="name"> | |
{({ input, meta }) => { | |
const controlClasses = cx({ | |
'form-control': true, | |
'is-invalid': meta.touched && meta.invalid && meta.error,, | |
}); | |
return ( | |
<FormGroup> | |
<label | |
htmlFor="name" | |
className="col-form-label col-form-label--required" | |
> | |
Имя | |
</label> | |
<input | |
{...input} | |
type={`text`} | |
className={controlClasses} | |
autoComplete={`off`} | |
/> | |
{meta.touched && | |
meta.error && ( | |
<div className="invalid-feedback">{meta.error}</div> | |
)} | |
<small | |
id="nameHelpBlock" | |
className="form-text form-text--help" | |
> | |
Мы же не можем обращаться к вам без имени, правда?! | |
</small> | |
</FormGroup> | |
); | |
}} | |
</Field> | |
<Field name="phone"> | |
{({ input, meta }) => { | |
const controlClasses = cx({ | |
'form-control': true, | |
'is-invalid': meta.touched && meta.invalid && meta.error,, | |
}); | |
return ( | |
<FormGroup> | |
<label | |
htmlFor="name" | |
className="col-form-label col-form-label--required" | |
> | |
Номер телефона | |
</label> | |
<InputMask | |
{...input} | |
type="tel" | |
name="phone" | |
id="phone" | |
mask="+7 (999) 999-99-99" | |
autoComplete="off" | |
className={controlClasses} | |
autoCapitalize="off" | |
autoCorrect="off" | |
placeholder="+7 (___) ___-__-__" | |
required | |
/> | |
{meta.touched && | |
meta.error && ( | |
<div className="invalid-feedback">{meta.error}</div> | |
)} | |
<small | |
id="nameHelpBlock" | |
className="form-text form-text--help" | |
> | |
Без номера телефона мы не сможем вам позвонить :) | |
</small> | |
</FormGroup> | |
); | |
}} | |
</Field> | |
{values ? ( | |
<pre>{JSON.stringify(values, 0, 2)}</pre> | |
) : ( | |
<FormSpy subscription={{ values: true }}> | |
{({ values }) => <pre>{JSON.stringify(values, 0, 2)}</pre>} | |
</FormSpy> | |
)} | |
<FormButtons portalSelector={buttonPortalId} | |
reset={reset} | |
submit={handleSubmit} | |
submitting={submitting} | |
pristine={pristine} /> | |
</form> | |
)} | |
/> | |
); | |
} | |
} | |
CallbackForm.propTypes = {}; | |
CallbackForm.defaultProps = {}; | |
export default CallbackForm; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment