Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Forms with React
Search
Radoslav Stankov
March 07, 2017
Technology
2
360
Forms with React
Tutorial on how to handle forms with React, presented at React.Sofia
Radoslav Stankov
March 07, 2017
Tweet
Share
More Decks by Radoslav Stankov
See All by Radoslav Stankov
Tips for Tailwind CSS
rstankov
0
5
Building LLM Powered Features (lightning talk)
rstankov
0
23
All you need is CSS
rstankov
0
76
Ruby on Rails The Single Engineer Framework
rstankov
0
18
Rails: The Missing Parts
rstankov
1
170
The dream that turned into nightmare
rstankov
0
270
The dream that turned into nightmare (lightning)
rstankov
0
90
Ruby on Rails - The Single Engineer Framework
rstankov
0
310
Living Without Exceptions
rstankov
1
270
Other Decks in Technology
See All in Technology
20251010_HCCJP_AdaptiveCloudUpdates
sdosamut
0
140
大規模サーバーレスAPIの堅牢性・信頼性設計 〜AWSのベストプラクティスから始まる現実的制約との向き合い方〜
maimyyym
10
5k
なぜAWSを活かしきれないのか?技術と組織への処方箋
nrinetcom
PRO
5
980
ソフトウェアエンジニアの生成AI活用と、これから
lycorptech_jp
PRO
0
490
やる気のない自分との向き合い方/How to Deal with Your Unmotivated Self
sanogemaru
1
520
Performance Insights 廃止から Database Insights 利用へ/transition-from-performance-insights-to-database-insights
emiki
0
310
プレーリーカードを活用しよう❗❗デジタル名刺交換からはじまるイベント会場交流のススメ
tsukaman
0
180
いまからでも遅くない!SSL/TLS証明書超入門(It's not too late to start! SSL/TLS Certificates: The Absolute Beginner's Guide)
norimuraz
0
270
All About Sansan – for New Global Engineers
sansan33
PRO
1
1.2k
AIとともに歩んでいくデザイナーの役割の変化
lycorptech_jp
PRO
0
480
能登半島地震で見えた災害対応の課題と組織変革の重要性
ditccsugii
0
1k
能登半島地震において デジタルができたこと・できなかったこと
ditccsugii
0
250
Featured
See All Featured
Building a Modern Day E-commerce SEO Strategy
aleyda
44
7.8k
YesSQL, Process and Tooling at Scale
rocio
173
14k
Unsuck your backbone
ammeep
671
58k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
5.6k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
667
120k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
A Tale of Four Properties
chriscoyier
161
23k
Into the Great Unknown - MozCon
thekraken
40
2.1k
Why Our Code Smells
bkeepers
PRO
340
57k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
16k
BBQ
matthewcrist
89
9.8k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
10
870
Transcript
Forms with React Radoslav Stankov 07/03/2017
Radoslav Stankov @rstankov http://rstankov.com http://github.com/rstankov
None
None
! https://github.com/tannerlinsley/react-form " https://github.com/prometheusresearch/react-forms # https://github.com/codecks-io/react-reform $ https://github.com/erikras/redux-form % https://github.com/Semantic-Org/Semantic-UI-React
Libraries
None
None
None
None
None
None
None
None
None
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label>
<input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label>
<input /> </div> <input type="submit" /> </form>
export default class SubmissionForm extends React.Component { render() { return
( <form method="post" action="/cfp/submission"> <h2>Speaker</h2> <div> <label htmlFor="speakerName">Name: </label> <input type="text" id="speakerName" name="speakerName" defaultValue="" /> </div> <div> <label htmlFor="speakerEmail">Email: </label> <input type="email" id="speakerEmail" name="speakerEmail" defaultValue="" /> </div> <input type="submit" value="Submit" /> </form> ); } }
export default class SubmissionForm extends React.Component { render() { return
( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <div> <label htmlFor="speakerName">Name: </label> <input type="text" id="speakerName" name="speakerName" defaultValue="" /> </div> <div> <label htmlFor="speakerEmail">Email: </label> <input type="email" id="speakerEmail" name="speakerEmail" defaultValue="" /> </div> <input type="submit" value="Submit" /> </form> ); } handleSubmit = (e) => { e.preventDefault(); const allValues = Array.from(e.target.elements).reduce((data, input) => { console.log(input); data[input.name] = input.value; return data; }, {}); remoteCall({ speakerName: allValues.speakerName, speakerEmail: allValues.speakerEmail, }); }; }
export default class SubmissionForm extends React.Component { render() { return
( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <div> <label htmlFor="speakerName">Name: </label> <input ref={(ref) => this.speakerName = ref} type="text" id="speakerName" name="speakerName" defaultV </div> <div> <label htmlFor="speakerEmail">Email: </label> <input ref={(ref) => this.speakerEmail = ref} type="email" id="speakerEmail" name="speakerEmail" defa </div> <input type="submit" value="Submit" /> </form> ); } handleSubmit = (e) => { e.preventDefault(); remoteCall({ speakerName: this.speakerName.value, speakerEmail: this.speakerEmail.value, }); }; }
export default class SubmissionForm extends React.Component { render() { return
( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <div> <label htmlFor="speakerName">Name: </label> <input ref={(ref) => this.speakerName = ref} type="text" id="speakerName" name="speakerName" defaultV </div> <div> <label htmlFor="speakerEmail">Email: </label> <input ref={(ref) => this.speakerEmail = ref} type="email" id="speakerEmail" name="speakerEmail" defa </div> <input type="submit" value="Submit" /> </form> ); } handleSubmit = (e) => { e.preventDefault(); remoteCall({ speakerName: this.speakerName.value, speakerEmail: this.speakerEmail.value, }); }; }
export default class SubmissionForm extends React.Component { render() { return
( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <div> <label htmlFor="speakerName">Name: </label> <input ref={(ref) => this.speakerName = ref} type="text" id="speakerName" name="speakerName" defaultV </div> <div> <label htmlFor="speakerEmail">Email: </label> <input ref={(ref) => this.speakerEmail = ref} type="email" id="speakerEmail" name="speakerEmail" defa </div> <input type="submit" value="Submit" /> </form> ); } handleSubmit = (e) => { e.preventDefault(); remoteCall({ speakerName: this.speakerName.value, speakerEmail: this.speakerEmail.value, }); }; } &
Uncontrolled Components • fast • simple • integration with external
libraries • no much control • gets messy quite easy
Controlled Components • you have more control • easier to
reason about • needed for complex form interactions • recommend by react documentation
class ExampleForm extends React.Component { state = { value: ''
} handleChange = (event) => { this.setState({value: event.target.value}); } handleSubmit = (event) => { e.preventDefault(); remoteCall(this.target.value)/ }; render() { return ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.value} onChange={this.handleChange} /> <input type="submit" value="Submit" /> </form> ); } }
class ExampleForm extends React.Component { state = { value: ''
} handleChange = (event) => { this.setState({value: event.target.value}); } handleSubmit = (event) => { e.preventDefault(); remoteCall(this.target.value)/ }; render() { return ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.value} onChange={this.handleChange} /> <input type="submit" value="Submit" /> </form> ); } } Input change
class ExampleForm extends React.Component { state = { value: ''
} handleChange = (event) => { this.setState({value: event.target.value}); } handleSubmit = (event) => { e.preventDefault(); remoteCall(this.target.value)/ }; render() { return ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.value} onChange={this.handleChange} /> <input type="submit" value="Submit" /> </form> ); } } Input change handleChange setState
class ExampleForm extends React.Component { state = { value: ''
} handleChange = (event) => { this.setState({value: event.target.value}); } handleSubmit = (event) => { e.preventDefault(); remoteCall(this.target.value)/ }; render() { return ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.value} onChange={this.handleChange} /> <input type="submit" value="Submit" /> </form> ); } } Input change handleChange setState render New value
<form> <h2> <div> <label> <input /> </div> <div> <label> <input
/> </div> <input type="submit" /> </form>
export default class SubmissionForm extends React.Component { state = {
fields: { speakerName: '', speakerEmail: '', }, }; render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <div> <label htmlFor="speakerName">Name: </label> <input value={this.state.fields.speakerName} onChange={this.handleChange} type="text" id="speakerName </div> <div> <label htmlFor="speakerEmail">Email: </label> <input value={this.state.fields.speakerEmail} onChange={this.handleChange} type="email" id="speakerEm </div> <input type="submit" value="Submit" /> </form> ); } handleChange = (e) => { const name = e.target.name; const value = e.target.value; this.setState({ fields: { ...this.state.fields, [name]: value } }); }; handleSubmit = (e) => { e.preventDefault(); remoteCall(this.state.fields); }; }
None
None
export default class SubmissionForm extends React.Component { state = {
fields: { speakerName: '', speakerEmail: '', }, isSumitting: false, }; render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <div> <label htmlFor="speakerName">Name: </label> <input value={this.state.fields.speakerName} onChange={this.handleChange} type="text" id="speakerName </div> <div> <label htmlFor="speakerEmail">Email: </label> <input value={this.state.fields.speakerEmail} onChange={this.handleChange} type="email" id="speakerEm </div> <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form> ); } handleChange = (e) => { /* … */ }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } this.setState({ isSubmitting: true }); await remoteCall(this.state.fields); }; }
export default class SubmissionForm extends React.Component { state = {
fields: { speakerName: '', speakerEmail: '', }, isSumitting: false, }; render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <div> <label htmlFor="speakerName">Name: </label> <input value={this.state.fields.speakerName} onChange={this.handleChange} type="text" id="speakerName </div> <div> <label htmlFor="speakerEmail">Email: </label> <input value={this.state.fields.speakerEmail} onChange={this.handleChange} type="email" id="speakerEm </div> <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form> ); } handleChange = (e) => { /* … */ }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } this.setState({ isSubmitting: true }); await remoteCall(this.state.fields); }; }
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <h2> <div> <label /> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <h2> <div> <label /> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <h2> <div> <label /> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <h2> <div> <label /> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <h2> <div> <label /> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <h2> <div> <label /> <input /> </div> <input type="submit" /> </form>
<form> <h2> <div> <label /> <input /> </div> <div> <label
/> <input /> </div> <h2> <div> <label /> <input /> </div> <input type="submit" /> </form>
export default class SubmissionForm extends React.Component { state = {
fields: { speakerName: '', speakerEmail: '', talkTitle: '', }, isSumitting: false, }; render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <div> <label htmlFor="speakerName">Name: </label> <input value={this.state.fields.speakerName} onChange={this.handleChange} type="text" id="speakerName </div> <div> <label htmlFor="speakerEmail">Email: </label> <input value={this.state.fields.speakerEmail} onChange={this.handleChange} type="email" id="speakerEm </div> <h2>Talk</h2> <div> <label htmlFor="talkTitle">Title: </label> <input type="text" id="talkTitle" name="talkTitle" value={this.state.fields.talkTitle} onChange={this </div> <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form> ); } handleChange = (e) => { /* … */ }; handleSubmit = (e) => { /* … */ }; }
export default class SubmissionForm extends React.Component { state = {
fields: { speakerName: '', speakerEmail: '', talkTitle: '', }, isSumitting: false, }; render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <div> <label htmlFor="speakerName">Name: </label> <input value={this.state.fields.speakerName} onChange={this.handleChange} type="text" id="speakerName </div> <div> <label htmlFor="speakerEmail">Email: </label> <input value={this.state.fields.speakerEmail} onChange={this.handleChange} type="email" id="speakerEm </div> <h2>Talk</h2> <div> <label htmlFor="talkTitle">Title: </label> <input type="text" id="talkTitle" name="talkTitle" value={this.state.fields.talkTitle} onChange={this </div> <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form> ); } handleChange = (e) => { /* … */ }; handleSubmit = (e) => { /* … */ }; }
export default class SubmissionForm extends React.Component { state = {
fields: { speakerName: '', speakerEmail: '', talkTitle: '', }, isSumitting: false, }; render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <div> <label htmlFor="speakerName">Name: </label> <input value={this.state.fields.speakerName} onChange={this.handleChange} type="text" id="speakerName </div> <div> <label htmlFor="speakerEmail">Email: </label> <input value={this.state.fields.speakerEmail} onChange={this.handleChange} type="email" id="speakerEm </div> <h2>Talk</h2> <div> <label htmlFor="talkTitle">Title: </label> <input type="text" id="talkTitle" name="talkTitle" value={this.state.fields.talkTitle} onChange={this </div> <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form> ); } handleChange = (e) => { /* … */ }; handleSubmit = (e) => { /* … */ }; }
let Field = ({ name, type, label, ...props }) =>
{ return ( <div> <label htmlFor={name}>{label}: </label> <input type={type || 'text'} id={name} name={name} {...props} /> </div> ); };
export default class SubmissionForm extends React.Component { state = {
/* … */ }; render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" value={this.state.fields.speakerName} onChange={thi <Field name="speakerEmail" label="Email" type="email" value={this.state.fields.speakerEmai <h2>Talk</h2> <Field name="talkTitle" label="Title" value={this.state.fields.talkTitle} onChange={this.h <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form> ); } handleChange = (e) => { /* … */ }; handleSubmit = (e) => { /* … */ }; }
export default class SubmissionForm extends React.Component { state = {
/* … */ }; render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" value={this.state.fields.speakerName} onChange={thi <Field name="speakerEmail" label="Email" type="email" value={this.state.fields.speakerEmai <h2>Talk</h2> <Field name="talkTitle" label="Title" value={this.state.fields.talkTitle} onChange={this.h <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form> ); } handleChange = (e) => { /* … */ }; handleSubmit = (e) => { /* … */ }; }
<form> <h2> <Field /> <Field /> <h2> <Field /> <submit
/> </form>
<form> <h2> <Field /> <Field /> <h2> <Field /> <Field
/> <submit /> </form>
<form> <h2> <Field /> <Field /> <h2> <Field /> <Field
/> <submit /> </form>
<form> <h2> <Field /> <Field /> <h2> <Field /> <Field
/> <submit /> </form> '
<form> <h2> <Field /> <Field /> <h2> <Field /> <Field
/> <submit /> </form> (
let Field = ({ name, input, label, ...props }) =>
{ if (input === 'textarea') { return ( <div> <label htmlFor={name}>{label}: </label> <textarea id={name} name={name} {...props} /> </div> ); } return ( <div> <label htmlFor={name}>{label}: </label> <input type={input || 'text'} id={name} name={name} {...props} /> </div> ); };
export default class SubmissionForm extends React.Component { state = {
fields: { speakerName: '', speakerEmail: '', talkTitle: '', talkDescription: '', }, isSumitting: false, }; render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" value={this.state.fields.speakerName} onChange={thi <Field name="speakerEmail" label="Email" input="email" value={this.state.fields.speakerEma <h2>Talk</h2> <Field name="talkTitle" label="Title" value={this.state.fields.talkTitle} onChange={this.h <Field name="talkDescription" label="Description" input="textarea" value={this.state.field <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form> ); } handleChange = (e) => { /* … */ }; handleSubmit = (e) => { /* … */ }; }
<form> <h2> <Field /> <Field /> <h2> <Field /> <Field
/> <submit /> </form>
<form> <h2> <Field /> <Field /> <h2> <Field /> <Field
/> <Field /> <submit /> </form>
<form> <h2> <Field /> <Field /> <h2> <Field /> <Field
/> <Field /> <submit /> </form>
const LENGTH_OPTIONS = [ {value: 15, label: '15 minutes'}, {value:
30, label: '30 minutes'}, {value: 45, label: '45 minutes'}, ];
<Field name="talkLength" label="Length" value={this.state.fields.talkLength} onChange={this.handleChange} input="select" options={LENGTH_OPTIONS} />
let Select = ({ options, ...props }) => ( <select
{...props}> {options.map(({ label, value }, i) => ( <option key={i} value={value}>{label || value}</option> ))} </select> );
let Field = ({ name, input, label, ...props }) =>
{ if (input === 'textarea') { return ( <div> <label htmlFor={name}>{label}: </label> <textarea id={name} name={name} {...props} /> </div> ); } if (input === 'select') { return ( <div> <label htmlFor={name}>{label}: </label> <Select id={name} name={name} {...props} /> </div> ); } return ( <div> <label htmlFor={name}>{label}: </label> <input type={input || 'text'} id={name} name={name} {...props} /> </div> ); };
let Input = ({ ...props }) => ( <input {...props}
/> );
const INPUTS = { 'textarea': Textarea, 'select': Select, 'text': Input,
}; let Field = ({ name, input = 'text', label, ...props }) => { const Component = INPUTS[input] || Input; const inputProps = Component === Input ? { type: input, ...props } : props; return ( <div> <label htmlFor={name}>{label}: </label> <Component id={name} name={name} {...inputProps} /> </div> ); };
const INPUTS = { 'textarea': Textarea, 'select': Select, 'text': Input,
}; let Field = ({ name, input = 'text', label, ...props }) => { const Component = typeof input === 'function' ? input : INPUTS[input] || Input; const inputProps = Component === Input ? { type: input, ...props } : props; return ( <div> <label htmlFor={name}>{label}: </label> <Component id={name} name={name} {...inputProps} /> </div> ); };
const INPUTS = { 'textarea': Textarea, 'select': Select, 'text': Input,
}; let Field = ({ name, input = 'text', label, ...props }) => { const Component = typeof input === 'function' ? input : INPUTS[input] || Input; const inputProps = Component === Input ? { type: input, ...props } : props; return ( <div> <label htmlFor={name}>{label}: </label> <Component id={name} name={name} {...inputProps} /> </div> ); };
<Field input={MyAwesomeCustomInput} />
None
<form> <h2> <Field /> <Field /> <h2> <Field /> <Field
/> <Field /> <submit /> </form>
<form> <h2> <Field /> <Field /> <h2> <Field /> <Field
/> <Field /> <Field /> <submit /> </form>
<form> <h2> <Field /> <Field /> <h2> <Field /> <Field
/> <Field /> <Field /> <submit /> </form>
let RadioGroup = ({ value: fValue, options, name, id, ...props
}) => ( <ul> {options.map(({ label, value }, i) => ( <li key={i}> <label> <input type="radio" name={name} value={value} checked={value === fValue} {...props} /> {label || value} </label> </li> ))} </ul> );
const INPUTS = { 'textarea': Textarea, 'select': Select, 'text': Input,
'radioGroup': RadioGroup, };
<form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" value={this.state.fields.speakerName} onChange={this.hand <Field name="speakerEmail"
label="Email" input="email" value={this.state.fields.speakerEmail} on <h2>Talk</h2> <Field name="talkTitle" label="Title" value={this.state.fields.talkTitle} onChange={this.handleI <Field name="talkDescription" label="Description" input="textarea" value={this.state.fields.talk <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} value={this.stat <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} value={th <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form>
<form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" value={this.state.fields.speakerName} onChange={this.hand <Field name="speakerEmail"
label="Email" input="email" value={this.state.fields.speakerEmail} on <h2>Talk</h2> <Field name="talkTitle" label="Title" value={this.state.fields.talkTitle} onChange={this.handleI <Field name="talkDescription" label="Description" input="textarea" value={this.state.fields.talk <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} value={this.stat <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} value={th <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form>
<form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" value={this.state.fields.speakerName} onChange={this.hand <Field name="speakerEmail"
label="Email" input="email" value={this.state.fields.speakerEmail} on <h2>Talk</h2> <Field name="talkTitle" label="Title" value={this.state.fields.talkTitle} onChange={this.handleI <Field name="talkDescription" label="Description" input="textarea" value={this.state.fields.talk <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} value={this.stat <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} value={th <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form>
<form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" /> <Field name="speakerEmail" label="Email"
input="email" /> <h2>Talk</h2> <Field name="talkTitle" label="Title" /> <Field name="talkDescription" label="Description" /> <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} /> <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} /> <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form>
Component
Component Child Component
Component Child Component Child Child Component
Component Child Component Child Child Component Child Child
… Child Component
Component Child Component Child Child Component Child Child
… Child Component Context
Component Child Component Child Child Component Child Child
… Child Component Context Context
export default class SubmissionForm extends React.Component { state = {
fields: { speakerName: '', speakerEmail: '', talkTitle: '', talkDescription: '', talkLength: '15', }, isSumitting: false, }; static childContextTypes = { formState: React.PropTypes.object.isRequired, formHandleChange: React.PropTypes.func.isRequired, }; getChildContext() { return { formState: this.state, formHandleChange: this.handleChange, }; } render() { /* … */ }; handleChange = (e) => { /* … */ }; handleSubmit = (e) => { /* … */ }; }
let Field = ({ name, input, label, ...props }, {
formState: { fields }, formHandleChange }) => { const value = fields[name]; const Component = typeof input === 'function' ? input : INPUTS[input] || Input; const inputProps = Component === Input ? { type: input, ...props } : props; return ( <div> <label htmlFor={name}>{label}: </label> <Component id={name} name={name} value={value} onChange={formHandleChange} {...inputProps} /> </div> ); }; Field.contextTypes = { formState: React.PropTypes.object.isRequired, formHandleChange: React.PropTypes.func.isRequired, };
let Field = ({ name, input, label, ...props }, {
formState: { fields }, formHandleChange }) => { const value = fields[name]; const Component = typeof input === 'function' ? input : INPUTS[input] || Input; const inputProps = Component === Input ? { type: input, ...props } : props; return ( <div> <label htmlFor={name}>{label}: </label> <Component id={name} name={name} value={value} onChange={formHandleChange} {...inputProps} /> </div> ); }; Field.contextTypes = { formState: React.PropTypes.object.isRequired, formHandleChange: React.PropTypes.func.isRequired, };
<form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" /> <Field name="speakerEmail" label="Email"
input="email" /> <h2>Talk</h2> <Field name="talkTitle" label="Title" /> <Field name="talkDescription" label="Description" /> <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} /> <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} /> <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form>
<form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" /> <Field name="speakerEmail" label="Email"
input="email" /> <h2>Talk</h2> <Field name="talkTitle" label="Title" /> <Field name="talkDescription" label="Description" /> <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} /> <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} /> <input type="submit" value="Submit" disabled={this.state.isSubmitting} /> </form>
let SubmitButton = ({ children }, { formState: { isSubmitting
}}) => ( <input type="submit" value={isSubmitting ? 'Submitting...' : children} disabled={isSubmitting} /> ); SubmitButton.contextTypes = { formState: React.PropTypes.object.isRequired, };
<form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" /> <Field name="speakerEmail" label="Email"
input="email" /> <h2>Talk</h2> <Field name="talkTitle" label="Title" /> <Field name="talkDescription" label="Description" /> <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} /> <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} /> <SubmitButton>Submit</SubmitButton> </form>
export default class SubmissionForm extends React.Component { state = {
fields: { speakerName: '', speakerEmail: '', talkTitle: '', talkDescription: '', talkLength: '15', }, isSumitting: false, }; static childContextTypes = { formState: React.PropTypes.object.isRequired, formHandleChange: React.PropTypes.func.isRequired, }; getChildContext() { return { formState: this.state, formHandleChange: this.handleChange, }; } render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" /> <Field name="speakerEmail" label="Email" input="email" /> <h2>Talk</h2> <Field name="talkTitle" label="Title" /> <Field name="talkDescription" label="Description" /> <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} /> <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} /> <SubmitButton>Submit</SubmitButton> </form>
<form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" /> <Field name="speakerEmail" label="Email"
input="email" /> <h2>Talk</h2> <Field name="talkTitle" label="Title" /> <Field name="talkDescription" label="Description" /> <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} /> <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} /> <SubmitButton>Submit</SubmitButton> </form> ); } handleChange = (e) => { const name = e.target.name; const value = e.target.value; const fields = { ...this.state.fields, [name]: value }; this.setState({ fields }); }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } this.setState({ isSubmitting: true }); await remoteCall(this.state.fields); }; }
export default class SubmissionForm extends React.Component { state = {
fields: { speakerName: '', speakerEmail: '', talkTitle: '', talkDescription: '', talkLength: '15', }, isSumitting: false, }; static childContextTypes = { formState: React.PropTypes.object.isRequired, formHandleChange: React.PropTypes.func.isRequired, }; getChildContext() { return { formState: this.state, formHandleChange: this.handleChange, }; } render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" /> <Field name="speakerEmail" label="Email" input="email" /> <h2>Talk</h2> <Field name="talkTitle" label="Title" /> <Field name="talkDescription" label="Description" /> <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} /> <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} /> <SubmitButton>Submit</SubmitButton> </form> ); } handleChange = (e) => { const name = e.target.name; const value = e.target.value; const fields = { ...this.state.fields, [name]: value }; this.setState({ fields }); }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } this.setState({ isSubmitting: true }); await remoteCall(this.state.fields); }; }
export default class SubmissionForm extends React.Component { state = {
fields: { speakerName: '', speakerEmail: '', talkTitle: '', talkDescription: '', talkLength: '15', }, isSumitting: false, }; static childContextTypes = { formState: React.PropTypes.object.isRequired, formHandleChange: React.PropTypes.func.isRequired, }; getChildContext() { return { formState: this.state, formHandleChange: this.handleChange, }; } render() { return ( <form onSubmit={this.handleSubmit}> <h2>Speaker</h2> <Field name="speakerName" label="Name" /> <Field name="speakerEmail" label="Email" input="email" /> <h2>Talk</h2> <Field name="talkTitle" label="Title" /> <Field name="talkDescription" label="Description" /> <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} /> <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} /> <SubmitButton>Submit</SubmitButton> </form> ); } handleChange = (e) => { const name = e.target.name; const value = e.target.value; const fields = { ...this.state.fields, [name]: value }; this.setState({ fields }); }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } this.setState({ isSubmitting: true }); await remoteCall(this.state.fields); }; }
class Form extends React.Component { state = { fields: this.props.fields,
isSumitting: false, }; static childContextTypes = { formState: React.PropTypes.object.isRequired, formHandleChange: React.PropTypes.func.isRequired, }; getChildContext() { return { formState: this.state, formHandleChange: this.handleChange, }; } render() { return ( <form onSubmit={this.handleSubmit}> {this.props.children} </form> ); } handleInputChange = (e) => { const name = e.target.name; const value = e.target.value; const fields = { ...this.state.fields, [name]: value }; this.setState({ fields }); };
return { formState: this.state, formHandleChange: this.handleChange, }; } render() {
return ( <form onSubmit={this.handleSubmit}> {this.props.children} </form> ); } handleInputChange = (e) => { const name = e.target.name; const value = e.target.value; const fields = { ...this.state.fields, [name]: value }; this.setState({ fields }); }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } this.setState({ isSubmitting: true }); await this.props.onSubmit(this.state.fields); }; }
const FIELDS = { speakerName: '', speakerEmail: '', talkTitle: '',
talkDescription: '', talkLength: '15', }; export default class SubmissionForm extends React.Component { render() { return ( <Form fields={FIELDS} onSubmit={remoteCall}> <h2>Speaker</h2> <Field name="speakerName" label="Name" /> <Field name="speakerEmail" label="Email" input="email" /> <h2>Talk</h2> <Field name="talkTitle" label="Title" /> <Field name="talkDescription" label="Description" input="textarea" /> <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} /> <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} /> <SubmitButton>Submit</SubmitButton> </Form> ); } }
const FIELDS = { speakerName: '', speakerEmail: '', talkTitle: '',
talkDescription: '', talkLength: '15', }; let SubmissionForm = () => ( <Form fields={FIELDS} onSubmit={remoteCall}> <h2>Speaker</h2> <Field name="speakerName" label="Name" /> <Field name="speakerEmail" label="Email" input="email" /> <h2>Talk</h2> <Field name="talkTitle" label="Title" /> <Field name="talkDescription" label="Description" input="textarea" /> <Field name="talkLength" label="Length" input="select" options={LENGTH_OPTIONS} /> <Field name="notifyVia" label="Notify me via" input="radioGroup" options={VIA_OPTIONS} /> <SubmitButton>Submit</SubmitButton> </Form> );
None
None
None
Submit
Submit Server
Submit Server Success
Submit Server Success Errors
remoteCall Server { result: 'ok' } { errors: {…} }
remoteCall Server { result: 'ok' } { errors: {
field1: [ 'error1', 'error2 field2: [ 'error1'] } }
{ errors: { field1: [ 'error1', 'error2' ], field2:
[ 'error1'] } }
class Form extends React.Component { state = { fields: this.props.fields,
isSumitting: false, }; static childContextTypes = { /* … */ }; getChildContext() = { /* … */ } render() = { /* … */ } handleInputChange = (e) => { /* … */ }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } await this.props.onSubmit(this.state.fields); }; }
class Form extends React.Component { state = { fields: this.props.fields,
errors: {}, isSumitting: false, }; static childContextTypes = { /* … */ }; getChildContext() = { /* … */ } render() = { /* … */ } handleInputChange = (e) => { /* … */ }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } this.setState({ isSubmitting: true, errors: {} }); const { errors } = await this.props.onSubmit(this.state.fields); this.setState({ isSubmitting: false, errors: errors || {} }); }; }
class Form extends React.Component { state = { fields: this.props.fields,
errors: {}, isSumitting: false, }; static childContextTypes = { /* … */ }; getChildContext() = { /* … */ } render() = { /* … */ } handleInputChange = (e) => { /* … */ }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } this.setState({ isSubmitting: true, errors: {} }); const { errors } = await this.props.onSubmit(this.state.fields); this.setState({ isSubmitting: false, errors: errors || {} }); }; }
let Field = ({ name, input, label, ...props }, {
formState: { fields, errors }, formHandleInputChange }) const value = fields[name]; const error = errors[name] && errors[name][0]; const Component = typeof input === 'function' ? input : INPUTS[input] || Input; const inputProps = Component === Input ? { type: input, ...props } : props; return ( <div> <label htmlFor={name}>{label}: </label> <Component id={name} name={name} value={value} onChange={formHandleInputChange} {...inputProps} /> { error && <strong>{error}</strong>} </div> ); };
let Field = ({ name, input, label, ...props }, {
formState: { fields, errors }, formHandleInputChange }) const value = fields[name]; const error = errors[name] && errors[name][0]; const Component = typeof input === 'function' ? input : INPUTS[input] || Input; const inputProps = Component === Input ? { type: input, ...props } : props; return ( <div> <label htmlFor={name}>{label}: </label> <Component id={name} name={name} value={value} onChange={formHandleInputChange} {...inputProps} /> { error && <strong>{error}</strong>} </div> ); };
class Form extends React.Component { state = { /* …
*/ }; static childContextTypes = { /* … */ }; getChildContext() = { /* … */ } render() = { /* … */ } handleInputChange = (e) => { /* … */ }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } this.setState({ isSubmitting: true, errors: {} }); const { errors } = await this.props.onSubmit(this.state.fields); this.setState({ isSubmitting: false, errors: errors || {} }); }; }
class Form extends React.Component { state = { /* …
*/ }; static childContextTypes = { /* … */ }; getChildContext() = { /* … */ } isUnmounted: boolean = false; componentWillUnmount() { this.isUnmounted = true; } render() = { /* … */ } handleInputChange = (e) => { /* … */ }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } this.setState({ isSubmitting: true, errors: {} }); const { errors } = await this.props.onSubmit(this.state.fields); if (!this.isUnmounted) { this.setState({ isSubmitting: false, errors: errors || {} }); } }; }
class Form extends React.Component { state = { /* …
*/ }; static childContextTypes = { /* … */ }; getChildContext() { /* … */ } isUnmounted: boolean = false; componentWillUnmount() { this.isUnmounted = true; } render() { /* … */ } handleChange = (e) => { /* … */ }; handleSubmit = async (e) => { e.preventDefault(); if (this.state.isSubmitting) { return; } this.setState({ isSubmitting: true, errors: {} }); const { errors } = await this.props.onSubmit(this.state.fields); if (!this.isUnmounted) { this.setState({ isSubmitting: false, errors: errors || {} }); } }; }
None
<Form> • simple form interface • extensible fields • protection
for double submit • standardized form layout • standardized server responses
None
What is missing? • client side validations • theming •
handling of deep nested forms • tests! • fun features ¯\_(ϑ)_/¯
! https://github.com/tannerlinsley/react-form " https://github.com/prometheusresearch/react-forms # https://github.com/codecks-io/react-reform $ https://github.com/erikras/redux-form % https://github.com/Semantic-Org/Semantic-UI-React
Libraries
None
https://github.com/RStankov/talks-code
https://speakerdeck.com/rstankov/forms-with-react
https://www.meetup.com/React-Sofia/
Thanks )
None