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
420
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
Building LLM Powered Features
rstankov
0
170
Tips for Tailwind CSS
rstankov
0
74
Building LLM Powered Features (lightning talk)
rstankov
0
87
All you need is CSS
rstankov
0
170
Ruby on Rails The Single Engineer Framework
rstankov
0
63
Rails: The Missing Parts
rstankov
1
270
The dream that turned into nightmare
rstankov
0
340
The dream that turned into nightmare (lightning)
rstankov
0
150
Ruby on Rails - The Single Engineer Framework
rstankov
0
370
Other Decks in Technology
See All in Technology
指示待ちから変化に応じるClaude Codeへ!~環境からAgentへの帰り道を作る~
gotalab555
9
1.8k
作り直せるコードは迅速に 作り直せないDBは慎重に - AI時代のプロダクトエンジニアが「判断の不可逆性」で開発速度を変える話
kinosuke01
0
110
[RSJ26] AnoleVLA: Lightweight Vision-Language-Action Model with Deep State Space Models for Mobile Manipulation
keio_smilab
PRO
0
120
Azure App Service / Container Apps の組み込み認証
kuniteru
0
170
2026-09-04 SRE Tech Talk #15 怠惰なTerraform / Lazy Terraform
masasuzu
0
120
現場に行くだけでは足りない——プロダクトエンジニアが業務の流れを捉える観点と、その鍛え方
takumiengineering
0
160
DMMブックスのNext.js化を加速させるAI活用 / Migrating DMM Books to Next.js with AI
kentarom
1
320
AIエージェントの開発・提供におけるセキュリティリスクの論点と対策
flatt_security
1
470
20260903 Tokyo Jazug Night #62 | Azure エンジニアよ、 その環境は本当にセキュアか?
olivia_0707
1
580
全社に広がるMCPサーバーを、 どう安全に管理するか MCPass開発の舞台裏
mtpooh
3
270
少人数データチームのDevin活用実践事例
runandy16
2
410
Beyond the Hype: Practical AI for Your Oracle Database with MCP
thatjeffsmith
1
190
Featured
See All Featured
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.4k
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
11k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
310
Designing Powerful Visuals for Engaging Learning
tmiket
1
530
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.3k
Discover your Explorer Soul
emna__ayadi
2
1.3k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
590
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4.3k
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