Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Forms with React
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Radoslav Stankov
March 07, 2017
Technology
430
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Forms with React
Tutorial on how to handle forms with React, presented at React.Sofia
Radoslav Stankov
March 07, 2017
More Decks by Radoslav Stankov
See All by Radoslav Stankov
Tips for Building Useful Agent Skills
rstankov
0
120
Building LLM Powered Features
rstankov
0
180
Tips for Tailwind CSS
rstankov
0
78
Building LLM Powered Features (lightning talk)
rstankov
0
94
All you need is CSS
rstankov
0
170
Ruby on Rails The Single Engineer Framework
rstankov
0
68
Rails: The Missing Parts
rstankov
1
280
The dream that turned into nightmare
rstankov
0
350
The dream that turned into nightmare (lightning)
rstankov
0
160
Other Decks in Technology
See All in Technology
Claude Codeを「使うほど育つ」AI秘書にするノウハウ
minorun365
PRO
27
22k
AI de Idea
kawaguti
PRO
2
110
銀行勘定系システムにおける開発プロセス刷新×AIによる環境モダナイゼーション / Development Process Transformation and AI-Driven Environment Modernization
muit
0
2.2k
アプリログインとWeb認証基盤をつなぐ ASWebAuthenticationSession 作法
shimastripe
1
320
synctest時代のhttptest Go 1.27で変わるHTTPサーバテストの裏側 / go conference2026 synctest and httptest
budougumi0617
1
3k
株式会社シーエーシー エンジニア向け会社紹介資料
cac
0
57k
20260912_スクフェス三河
kgnkhkr
0
380
現場で役立つ技術負債の効果的な返済方法
masuda220
PRO
8
4.1k
AIエージェントの自己改善をどう設計するか / How to Design Self-Improvement for AI Agents
22mi
23
15k
アプリをもっと"iOSアプリっぽく"する小さな工夫 / Small Touches That Make Your App Feel More Like an iOS App
matsuji
1
900
Claude Code本って、 読む必要あるの?
oikon48
2
460
Issue 駆動でスペシャリストの意図を届ける、AI 実装のアクセシビリティ向上
thkt
0
110
Featured
See All Featured
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
540
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
600
How to build a perfect <img>
jonoalderson
1
6k
Heart Work Chapter 1 - Part 1
lfama
PRO
10
36k
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
540
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
200
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
203
76k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
4.8k
Between Models and Reality
mayunak
4
460
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.3k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
520
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