SlideShare a Scribd company logo
REACT FORMS
... a crime scene ...
CRIME SCENE - DO NOT CROSS
mehiel@DOM
React, since 2015
Logicea, since 2013
CanJS, since 2011
ExtJS, since 2010
jQuery, since 2008
Web, since 2004
Forms: why bother?
Inevitable
Still a Pain
Business Interest
Fun?
Forms: pwn factor
|-------------------------------------------∞
^ ^ ^ ^
html react TODAY pwned
Forms: watch for
the Designer
the User
the API
the Browser
Forms: ingredients
Flexible Layout
Variety of Inputs
Dynamic Fields
Dynamic Values
Field Dependencies
Lookup Data
Validation
Hints & Errors
Field Status
Requests & Responses
React: the weapon
Declarative
Painless interactive UIs.
Design views for each state in your app
Predictable and easier to debug
Component Based
Encapsulated with own state
Composable to make complex UIs
Keep state out of the DOM
// the simplest react component as of React 16
function WorldGreeter() {
return "Hello, World"
}
<WorldGreeter /> => "Hello, World"
// with some JSX sauce
function FriendGreeter({ name }) {
return <div>Hello {name}!</div>
}
<FriendGreeter name="Bob" /> => "Hello Bob!"
class NobleGreeter extends React.Component {
// componentLifecycleFunctions()
// this.setState()
render() {
return (
<div>
Hello {this.props.title} {this.props.name}
</div>
)
}
}
<NobleGreeter title="sir" name="William" /> => "Hello sir William"
REACT FORMS
a crime in 3 acts
ACT 1
the victim
Act 1 : Scene 1 : ■
function ProfileForm1() {
return (
<form>
<input type="text" name="username" />
<input type="text" name="email" />
<input type="password" name="password" />
</form>
)
}
Act 1 : Scene 1 : ▸
Act 1 : Scene 2 : ■
<div>
<label htmlFor="username">Username</label>
<input type="text" name="username" id="username" />
</div>
<div>
<label htmlFor="email">E-mail</label>
<input type="text" name="email" id="email" />
</div>
<div>
<label htmlFor="password">Password</label>
<input type="password" name="password" id="password" />
</div>
Act 1 : Scene 2 : ▸
Username
E-mail
Password
Act 1 : Scene 3 : ■
export class ProfileForm3 extends Component {
onSubmit = (ev) => {
ev.preventDefault() // disable browser post
const data = { ... }
fetch('/api/me', { method: 'POST', body: ... })
}
inputs = {}
render() {
return (<form onSubmit={this.onSubmit}> ... </form>)
}
}
Act 1 : Scene 3 : ■
inputs = {}
onSubmit = (ev) => {...
const data = {
username: this.inputs.username.value,
}
}
<form onSubmit={this.onSubmit}>...
<input type="text" name="username" id="username"
ref={(el) => this.inputs.username = el} />
</form>
Act 1 : Scene 3 : ▸
Username
E-mail
Password
Submit
Act 1 : Scene 4 : ■
export class ProfileForm4 extends Component {
onSubmit = (ev) => {...
const data = {}
new FormData(ev.target).forEach(
(value, name) => data[name] = value
)
...}
render() {
...
<input type="text" name="username" id="username" />
}
}
Act 1 : Scene 4 : ▸
Username
E-mail
Password
Submit
Break : Controlled Input : ⚛
With controlled components react state is the “single source of truth”.
<input type="text" value="GreeceJS" />
GreeceJS
<input type="text" value={name} onChange={handleNameChange} />
Break : Controlled Input : ⚛
feature uncontrolled controlled
on submit value retrieval ✅ ✅
validating on submit ✅ ✅
instant eld validation ❌ ✅
conditionally disabling inputs ❌ ✅
enforcing input format ❌ ✅
dynamic inputs ❌ ✅
Act 1 : Scene 5 : ■
export class ProfileForm5 extends Component {
state = {}
onUsernameChange = (ev) => {
const value = ev.target.value
this.setState(prevState =>
({ ...prevState, username: value })) // async
}
onEmailChange = (ev) => { ... }
onPasswordChange = (ev) => { ... }
onSubmit = (ev) => {
const data = this.state
post('/api/me', data)
}
render() { ... }
}
Act 1 : Scene 5 : ■
render() {
const { username = '', email = '', password = '' } = this.state
// undefined values will mark inputs as uncontrolled
// -> unintented use -> maybe bugs -> react warns
return (
<form onSubmit={this.onSubmit}>
<div>
<label htmlFor="username">Username</label>
<input type="text" name="username" id="username"
value={username} onChange={this.onUsernameChange} />
</div>
...
}
Act 1 : Scene 5 : ▸
Username
E-mail
Password
Submit
Act 1 : Scene 6 : ■
export class ProfileForm6 extends Component {
state = { username: '', email: '', password: '' }
onSubmit = (ev) => {...}
onChange = (ev) => {
const { name, value } = ev.target
this.setState(prevState => ({ ...prevState, [name]: value }))
}
render() {
const { username, email, password } = this.state
...
<input type="text" name="username" id="username"
value={username} onChange={this.onChange} />
}
}
Act 1 : Scene 6 : ▸
Username
E-mail
Password
Submit
ACT 2
set the stage
Break : Container Components : ⚛
You’ll nd your components much easier to reuse and reason about if you divide
them into two categories.
I call them Container and Presentational components
- Dan Abramov
Break : Container Components : ⚛
Concerned with how things work
Provide the data and behavior
Call ux actions and provide callbacks
Often stateful, as they tend to serve as data sources
Usually generated using higher order components
Act 2 : Scene 1 : ■
export const ProfileContainer = Child => {
class Container extends React.Component {
state = { values: {}, errors: {} };
onSubmit = ev => post('/api/me', this.state.values)
onChange = (values, errors) => {
this.setState(prevState => ({ ...prevState, values, errors }));
};
render() {
const { children, ...childProps } = this.props;
const { values, errors } = this.state;
const { onChange, onSubmit } = this;
return (
<Child {...childProps}
form={{values, errors, onChange, onSubmit}}>
{children}</Child>
)}}
return Container;
}}
Act 2 : Scene 1 : ■
class _ProfileForm7 extends Component {
onChange = (ev) => {
const { values, onChange } = this.props.form
const { name, value } = ev.target
const newValues = { ...values, [name]: value }
onChange(newValues)
}
render() {
const { values, onSubmit } = this.props.form
return <form onSubmit={onSubmit}>...
<input type="text" name="username" id="username"
value={values.username || ''} onChange={this.onChange} />
}}
const ProfileForm7 = ProfileContainer(_ProfileForm7)
export default ProfileForm7
Act 2 : Scene 1 : ▸
Username
E-mail
Password
Submit
Act 2 : Scene 2 : ■
<form onSubmit={onSubmit}>
<div>
<label htmlFor="username">Username</label>
<input type="text" name="username" id="username"
value={values.username || ''} onChange={this.onChange} />
</div>
<div>
<label htmlFor="email">E-mail</label>
<input type="text" name="email" id="email"
value={values.email || ''} onChange={this.onChange} />
</div>
<div>
<label htmlFor="password">Password</label>
<input type="password" name="password" id="password"
value={values.password || ''} onChange={this.onChange} />
</div>
<input type="submit" />
</form>
Act 2 : Scene 2 : ■
class Fields extends Component {
...
}
Act 2 : Scene 2 : ■
class _ProfileForm8 extends Component {
schema = [{...}, {...}, {...}]
render() {
const { values, onChange, onSubmit } = this.props.form
return (
<form onSubmit={onSubmit}>
<Fields schema={this.schema}
values={values} onChange={onChange} />
<input type="submit" />
</form>
)
}
}
const ProfileForm8 = ProfileContainer(_ProfileForm8)
export { ProfileForm8 }
Act 2 : Scene 2 : ■
class Fields extends Component {
onChangeHandler = ev => {
const { values, onChange } = this.props;
const { name, value } = ev.target;
const newValues = {...values, [name]: (value || null) };
onChange(newValues);
};
render() {
const { schema, values } = this.props;
return (<div>...</div>);
}
}
Act 2 : Scene 2 : ■
schema = [{
path: "username",
label: "Username",
renderer: (path, value, label, onChange) => {
return (
<div>
<label htmlFor="username">Username</label>
<input type="text" name="username" id="username"
value={value} onChange={onChange} />
</div>
)
}
}]
Act 2 : Scene 2 : ■
class Fields extends Component {
onChangeHandler = ...
render() {
const { schema, values } = this.props;
return (<div>
{schema.map(field =>
field.renderer(
field.path,
values[field.path] || '',
field.label,
this.onChangeHandler)
)}
</div>);
}}
Act 2 : Scene 2 : ■
class _ProfileForm8 extends Component {
schema = [{...}, {...}, {...}]
render() {
const { values, onChange, onSubmit } = this.props.form
return (
<form onSubmit={onSubmit}>
<Fields schema={this.schema}
values={values} onChange={onChange} />
<input type="submit" />
</form>
)
}
}
const ProfileForm8 = ProfileContainer(_ProfileForm8)
export { ProfileForm8 }
Act 2 : Scene 2 : ▸
Username
E-mail
Password
Submit
Act 2 : Scene 3 : ■
schema = [{
path: "username",
label: "Username",
renderer: (path, value, label, onChange) => {
return (
<div>
<label htmlFor="username">Username</label>
<input type="text" name="username" id="username"
value={value} onChange={onChange} />
</div>
)
}
}]
Act 2 : Scene 3 : ■
const textRenderer = (path, value, label, onChange) => (
<div>
<label htmlFor={path}>{label}</label>
<input type="text" name={path} id={path}
value={value} onChange={onChange} />
</div>
)
schema = [
{ path: "username", label: "Username", renderer: textRenderer }
]
Act 2 : Scene 3 : ■
const textRenderer = (type = 'text') =>
(path, value, label, onChange) => (
<div>
<label htmlFor={path}>{label}</label>
<input type={type} name={path} id={path}
value={value} onChange={onChange} />
</div>
)
const passwordRenderer = () => textRenderer('password')
schema = [
{path: "username", label: "Username", renderer: textRenderer()},
{path: "email", label: "E-mail", renderer: textRenderer()},
{path: "password", label: "Password", renderer: passwordRenderer()}
]
Act 2 : Scene 3 : ■
class _ProfileForm9 extends Component {
schema = [
{ path: "username", label: ..., renderer: textRenderer() },
{ path: "email", label: ..., renderer: textRenderer() },
{ path: "password", label: ..., renderer: passwordRenderer() }
]
render() {
const { values, onChange, onSubmit } = this.props.form
return (
<form onSubmit={onSubmit}>
<Fields schema={this.schema}
values={values} onChange={onChange} />
<input type="submit" />
</form>
)
}
}
const ProfileForm9 = ProfileContainer(_ProfileForm9)
export { ProfileForm9 }
Act 2 : Scene 3 : ▸
Username
E-mail
Password
Submit
Act 2 : Scene 4 : ■
class _ProfileForm10 extends Component {
schema = (values) => {
const _schema = [
{ path: "username", label: ..., renderer: textRenderer() },
{ path: "email", label: ..., renderer: textRenderer() }
]
if (values.email) _schema.push(
{ path: "password", label: ..., renderer: passwordRenderer() }
)
return _schema;
}
render() {
const { values, onChange, onSubmit } = this.props.form
return ( ... <Fields schema={this.schema(values)} ... )
}
}
Act 2 : Scene 4 : ▸
Username
E-mail
Submit
ACT 3
the perfect crime!
yeah, well! Not yet in the public but...
yarn add @logicea/react-forms
Act 3 : Scene 1 : ■
export const ProfileContainer = Child => {
class Container extends React.Component {
state = { values: {}, errors: {} };
onSubmit = ev => post('/api/me', this.state.values)
onChange = (values, errors) => {
this.setState(prevState => ({ ...prevState, values, errors }));
};
render() {
const { children, ...childProps } = this.props;
const { values, errors } = this.state;
const { onChange, onSubmit } = this;
return (
<Child {...childProps}
form={{values, errors, onChange, onSubmit}}>
{children}</Child>
)}}
return Container;
}}
Act 3 : Scene 1 : ■
onChange = ev => {
const { values, errors } = ev.detail
this.setState(prevState => ({ ...prevState, values, errors }));
};
Act 3 : Scene 1 : ■
import Fields from '@logicea/react-forms/Fields'
class _ProfileForm11 extends Component {
schema = (values) => {
const _schema = [
{ row: 1, path: "username", ... },
{ row: 2, path: "email", ... }
]
if(...) _schema.push({ row: 3, path: "password", ... }) })
}
render() { ... }
}
Act 3 : Scene 1 : ▸
Username
E-mail
Submit
Act 3 : Scene 2 : ■
import Fields from '@logicea/react-forms/Fields'
import yup from 'yup'
class _ProfileForm12 extends Component {
schema = [
{ path: "username", validator: yup.string().required(), ... },
{ path: "email", validator: yup.string().email(), ... },
{ path: "password", validator: yup.string().min(4), ... },
]
render() {
const { values, errors, onChange, onSubmit } = this.props.form
return ... <Fields schema={this.schema(values)}
values={values} errors={errors} onChange={onChange} />
}
}
Act 3 : Scene 2 : ■
const textRenderer = (type = 'text') =>
(path, value, label, onChange, error) => (
<div>
<label>...</label>
<input ... />
{error && <div style={{ color: 'red' }}>{error}</div>}
</div>
)
const passwordRenderer = () => textRenderer('password')
Act 3 : Scene 2 : ▸
Username
E-mail
Password
Submit
Act 3 : Scene 3 : ■
class _ProfileForm13 extends Component {
schema = (values) => {
const _schema = [
...
{ row: 4, path: "country", label: "Country",
dependands: ["city"], renderer: textRenderer() },
{ row: 5, path: "city", label: "City",
renderer: textRenderer() },
]
return _schema;
}
render() { ... }
}
Act 3 : Scene 3 : ▸
Username
E-mail
Password
Country
City
Submit
Act 3 : Scene 4 : ■
const COUNTRIES = [
'Greece',
'Burundi',
'Democratic Republic of Congo',
'Central African Republic'
]
const CITIES = {
'Greece': ['Athens', 'Thessaloniki'],
'Burundi': ['Bujumbura', 'Muyinga'],
'Democratic Republic of Congo': ['Kinshasa', 'Lubumbashi'],
'Central African Republic': ['Bangui', 'Bimbo']
}
Act 3 : Scene 4 : ■
const selectRenderer = (options = [])
=> (path, value, label, onChange, error) => (
<div>
<label htmlFor={path}>{label}</label>
<select name={path} id={path}
value={value} onChange={onChange}>
<option value=''></option>
{options.map(o => <option value={o} key={o}>{o}</option>)}
</select>
{error && <div style={{ color: 'red' }}>{error}</div>}
</div>
)
}
Act 3 : Scene 4 : ■
class _ProfileForm14 extends Component {
schema = (values, allCountries, allCities) => {
const cities = values.country ? allCities[values.country] : []
const _schema = [...
{ path: "country", renderer: selectRenderer(allCountries) ...
{ path: "city", renderer: selectRenderer(cities) ...
]
return _schema;
}
render() {
const schema = this.schema(values, COUNTRIES, CITIES)
...
}
}
Act 3 : Scene 4 : ▸
Username
E-mail
Password
Country
City
Submit
Act 3 : Scene 5 : ■
const getCountries = (props) => {
return new Promise(r => setTimeout(() => r(COUNTRIES), 200))
}
const getCities = (props) => {
const selectedCountry = props.form.values.country
const citiesResponse = CITIES[selectedCountry] || []
return new Promise(r => setTimeout(() => r(citiesResponse), 200))
}
Act 3 : Scene 5 : ■
class _ProfileForm15 extends Component {
schema = (values, countries, countryCities) => [...
{ path: "country", renderer: selectRenderer(countries) ...
{ path: "city", renderer: selectRenderer(countryCities) ...
]
render() {...
const { countries, cities } = this.props.lookups
const schema = this.schema(values, countries, cities)
}
}
const ProfileForm15 = compose(ProfileContainer(), Lookups({
initial: ['countries'],
resolver: (lookupField) => { ... },
rules: (oldProps, newProps) => { ... },
}))(_ProfileForm15)
export { ProfileForm15 }
Act 3 : Scene 5 : ■
const lookupsResolver = (lookupField) => {
switch (lookupField) { // eslint-disable-line default-case
case 'countries': return getCountries;
case 'cities': return getCities;
}
}
const lookupRules = (oldProps, newProps) => {
let lookups = []
const countryChanged =
oldProps.form.values.country !== newProps.form.values.country
if (countryChanged) lookups.push('cities')
return { lookups }
}
Act 3 : Scene 5 : ▸
Username
E-mail
Password
Country
City
Submit
Forms: other libs
complex
feature full
bloated
simple
state mgmt
redux-free
our inspiration
joi based
not mature
react-joi-forms
Performance
Performance - @logicea/react-forms
Performance - Formik
Performance - Formik vs Redux Form
Taming forms with React

More Related Content

What's hot

How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
Samuel ROZE
 
Юрий Буянов «Squeryl — ORM с человеческим лицом»
Юрий Буянов «Squeryl — ORM с человеческим лицом»Юрий Буянов «Squeryl — ORM с человеческим лицом»
Юрий Буянов «Squeryl — ORM с человеческим лицом»e-Legion
 
How to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI ComponentsHow to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI Components
cagataycivici
 
Symfony2. Form and Validation
Symfony2. Form and ValidationSymfony2. Form and Validation
Symfony2. Form and Validation
Vladimir Doroshenko
 
Implementation of EAV pattern for ActiveRecord models
Implementation of EAV pattern for ActiveRecord modelsImplementation of EAV pattern for ActiveRecord models
Implementation of EAV pattern for ActiveRecord models
Kostyantyn Stepanyuk
 
Command-Oriented Architecture
Command-Oriented ArchitectureCommand-Oriented Architecture
Command-Oriented Architecture
Luiz Messias
 
Improving state of M2 front-end - Magento 2 Community Project
Improving state of M2 front-end - Magento 2 Community ProjectImproving state of M2 front-end - Magento 2 Community Project
Improving state of M2 front-end - Magento 2 Community Project
Bartek Igielski
 
EAV Sytem- Magento EAV Model
EAV Sytem- Magento EAV ModelEAV Sytem- Magento EAV Model
EAV Sytem- Magento EAV Model
Khoa Truong Dinh
 
Jquery In Rails
Jquery In RailsJquery In Rails
Jquery In Rails
shen liu
 
Web 6 | JavaScript DOM
Web 6 | JavaScript DOMWeb 6 | JavaScript DOM
Web 6 | JavaScript DOM
Mohammad Imam Hossain
 
Drupal csu-open atriumname
Drupal csu-open atriumnameDrupal csu-open atriumname
Drupal csu-open atriumname
Emanuele Quinto
 
Modern JavaScript Engine Performance
Modern JavaScript Engine PerformanceModern JavaScript Engine Performance
Modern JavaScript Engine PerformanceCatalin Dumitru
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patterns
Łukasz Chruściel
 
Building Persona: federated and privacy-sensitive identity for the Web (Open ...
Building Persona: federated and privacy-sensitive identity for the Web (Open ...Building Persona: federated and privacy-sensitive identity for the Web (Open ...
Building Persona: federated and privacy-sensitive identity for the Web (Open ...
Francois Marier
 
Web 5 | JavaScript Events
Web 5 | JavaScript EventsWeb 5 | JavaScript Events
Web 5 | JavaScript Events
Mohammad Imam Hossain
 
Open Selector
Open SelectorOpen Selector
Open Selectorjjdelc
 
Eureka - elegant iOs form builder in swift (johnp - mingle)
Eureka - elegant iOs form builder in swift (johnp - mingle)Eureka - elegant iOs form builder in swift (johnp - mingle)
Eureka - elegant iOs form builder in swift (johnp - mingle)
John Pham
 
Quill vs Slick Smackdown
Quill vs Slick SmackdownQuill vs Slick Smackdown
Quill vs Slick Smackdown
Alexander Ioffe
 

What's hot (20)

How I started to love design patterns
How I started to love design patternsHow I started to love design patterns
How I started to love design patterns
 
HTML Form Part 1
HTML Form Part 1HTML Form Part 1
HTML Form Part 1
 
Юрий Буянов «Squeryl — ORM с человеческим лицом»
Юрий Буянов «Squeryl — ORM с человеческим лицом»Юрий Буянов «Squeryl — ORM с человеческим лицом»
Юрий Буянов «Squeryl — ORM с человеческим лицом»
 
How to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI ComponentsHow to Mess Up Your Angular UI Components
How to Mess Up Your Angular UI Components
 
Symfony2. Form and Validation
Symfony2. Form and ValidationSymfony2. Form and Validation
Symfony2. Form and Validation
 
Implementation of EAV pattern for ActiveRecord models
Implementation of EAV pattern for ActiveRecord modelsImplementation of EAV pattern for ActiveRecord models
Implementation of EAV pattern for ActiveRecord models
 
Command-Oriented Architecture
Command-Oriented ArchitectureCommand-Oriented Architecture
Command-Oriented Architecture
 
Improving state of M2 front-end - Magento 2 Community Project
Improving state of M2 front-end - Magento 2 Community ProjectImproving state of M2 front-end - Magento 2 Community Project
Improving state of M2 front-end - Magento 2 Community Project
 
EAV Sytem- Magento EAV Model
EAV Sytem- Magento EAV ModelEAV Sytem- Magento EAV Model
EAV Sytem- Magento EAV Model
 
Jquery In Rails
Jquery In RailsJquery In Rails
Jquery In Rails
 
Web 6 | JavaScript DOM
Web 6 | JavaScript DOMWeb 6 | JavaScript DOM
Web 6 | JavaScript DOM
 
Drupal csu-open atriumname
Drupal csu-open atriumnameDrupal csu-open atriumname
Drupal csu-open atriumname
 
Modern JavaScript Engine Performance
Modern JavaScript Engine PerformanceModern JavaScript Engine Performance
Modern JavaScript Engine Performance
 
Django quickstart
Django quickstartDjango quickstart
Django quickstart
 
Symfony World - Symfony components and design patterns
Symfony World - Symfony components and design patternsSymfony World - Symfony components and design patterns
Symfony World - Symfony components and design patterns
 
Building Persona: federated and privacy-sensitive identity for the Web (Open ...
Building Persona: federated and privacy-sensitive identity for the Web (Open ...Building Persona: federated and privacy-sensitive identity for the Web (Open ...
Building Persona: federated and privacy-sensitive identity for the Web (Open ...
 
Web 5 | JavaScript Events
Web 5 | JavaScript EventsWeb 5 | JavaScript Events
Web 5 | JavaScript Events
 
Open Selector
Open SelectorOpen Selector
Open Selector
 
Eureka - elegant iOs form builder in swift (johnp - mingle)
Eureka - elegant iOs form builder in swift (johnp - mingle)Eureka - elegant iOs form builder in swift (johnp - mingle)
Eureka - elegant iOs form builder in swift (johnp - mingle)
 
Quill vs Slick Smackdown
Quill vs Slick SmackdownQuill vs Slick Smackdown
Quill vs Slick Smackdown
 

Similar to Taming forms with React

Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
Samuel ROZE
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
Daniel Knell
 
Architecting JavaScript Code
Architecting JavaScript CodeArchitecting JavaScript Code
Architecting JavaScript CodeSuresh Balla
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Fabien Potencier
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
Jorn Oomen
 
Intoduction on Playframework
Intoduction on PlayframeworkIntoduction on Playframework
Intoduction on Playframework
Knoldus Inc.
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
Mariusz Kozłowski
 
Mashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsMashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web Apps
Bastian Hofmann
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
Ryunosuke SATO
 
Rails GUI Development with Ext JS
Rails GUI Development with Ext JSRails GUI Development with Ext JS
Rails GUI Development with Ext JS
Martin Rehfeld
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
Nishan Subedi
 
PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2
eugenio pombi
 
Secrets of JavaScript Libraries
Secrets of JavaScript LibrariesSecrets of JavaScript Libraries
Secrets of JavaScript Libraries
jeresig
 
Data20161007
Data20161007Data20161007
Data20161007
capegmail
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说Ting Lv
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
Jeroen van Dijk
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
Michelangelo van Dam
 
Gutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisablesGutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisables
Riad Benguella
 

Similar to Taming forms with React (20)

Symfony CoP: Form component
Symfony CoP: Form componentSymfony CoP: Form component
Symfony CoP: Form component
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
Architecting JavaScript Code
Architecting JavaScript CodeArchitecting JavaScript Code
Architecting JavaScript Code
 
Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)Be RESTful (Symfony Camp 2008)
Be RESTful (Symfony Camp 2008)
 
Crafting beautiful software
Crafting beautiful softwareCrafting beautiful software
Crafting beautiful software
 
Intoduction on Playframework
Intoduction on PlayframeworkIntoduction on Playframework
Intoduction on Playframework
 
Mashing up JavaScript
Mashing up JavaScriptMashing up JavaScript
Mashing up JavaScript
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Mashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsMashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web Apps
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
Rails GUI Development with Ext JS
Rails GUI Development with Ext JSRails GUI Development with Ext JS
Rails GUI Development with Ext JS
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2PHPUnit elevato alla Symfony2
PHPUnit elevato alla Symfony2
 
Secrets of JavaScript Libraries
Secrets of JavaScript LibrariesSecrets of JavaScript Libraries
Secrets of JavaScript Libraries
 
Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2
 
Data20161007
Data20161007Data20161007
Data20161007
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
Introduction to Zend Framework web services
Introduction to Zend Framework web servicesIntroduction to Zend Framework web services
Introduction to Zend Framework web services
 
Gutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisablesGutenberg sous le capot, modules réutilisables
Gutenberg sous le capot, modules réutilisables
 

More from GreeceJS

Introduction to react-query. A Redux alternative? (Nikos Kleidis, Front End D...
Introduction to react-query. A Redux alternative? (Nikos Kleidis, Front End D...Introduction to react-query. A Redux alternative? (Nikos Kleidis, Front End D...
Introduction to react-query. A Redux alternative? (Nikos Kleidis, Front End D...
GreeceJS
 
Next.js and the pursuit of happiness (Dimitris Michalakos, Lead Developer at ...
Next.js and the pursuit of happiness (Dimitris Michalakos, Lead Developer at ...Next.js and the pursuit of happiness (Dimitris Michalakos, Lead Developer at ...
Next.js and the pursuit of happiness (Dimitris Michalakos, Lead Developer at ...
GreeceJS
 
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...
GreeceJS
 
Under the hood of RxJS (Dimitris Livas) - GreeceJS #27
Under the hood of RxJS (Dimitris Livas) - GreeceJS #27Under the hood of RxJS (Dimitris Livas) - GreeceJS #27
Under the hood of RxJS (Dimitris Livas) - GreeceJS #27
GreeceJS
 
All About GRAND Stack: GraphQL, React, Apollo, and Neo4j (Mark Needham) - Gre...
All About GRAND Stack: GraphQL, React, Apollo, and Neo4j (Mark Needham) - Gre...All About GRAND Stack: GraphQL, React, Apollo, and Neo4j (Mark Needham) - Gre...
All About GRAND Stack: GraphQL, React, Apollo, and Neo4j (Mark Needham) - Gre...
GreeceJS
 
Cross platform engineering - Lessons Learned (Michael Asimakopoulos, Valadis ...
Cross platform engineering - Lessons Learned (Michael Asimakopoulos, Valadis ...Cross platform engineering - Lessons Learned (Michael Asimakopoulos, Valadis ...
Cross platform engineering - Lessons Learned (Michael Asimakopoulos, Valadis ...
GreeceJS
 
TypeScript: JavaScript that scales (Kostas Stergiou) - GreeceJS #22
TypeScript: JavaScript that scales (Kostas Stergiou) - GreeceJS #22TypeScript: JavaScript that scales (Kostas Stergiou) - GreeceJS #22
TypeScript: JavaScript that scales (Kostas Stergiou) - GreeceJS #22
GreeceJS
 
Migrating from Monolithic to Serverless (Kostas Katsikas) - GreeceJS #22
Migrating from Monolithic to Serverless (Kostas Katsikas) - GreeceJS #22Migrating from Monolithic to Serverless (Kostas Katsikas) - GreeceJS #22
Migrating from Monolithic to Serverless (Kostas Katsikas) - GreeceJS #22
GreeceJS
 
The JavaScript toolset for development on Ethereum
The JavaScript toolset for development on EthereumThe JavaScript toolset for development on Ethereum
The JavaScript toolset for development on Ethereum
GreeceJS
 
Modern web development and accessibility (Christina Papadimitriou, Nadia Mark...
Modern web development and accessibility (Christina Papadimitriou, Nadia Mark...Modern web development and accessibility (Christina Papadimitriou, Nadia Mark...
Modern web development and accessibility (Christina Papadimitriou, Nadia Mark...
GreeceJS
 
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
GreeceJS
 
React Native and the future of web technology (Mark Wilcox) - GreeceJS #15
React Native and the future of web technology (Mark Wilcox) - GreeceJS #15React Native and the future of web technology (Mark Wilcox) - GreeceJS #15
React Native and the future of web technology (Mark Wilcox) - GreeceJS #15
GreeceJS
 
The rise of Polymer and Web Components (Kostas Karolemeas) - GreeceJS #17
The rise of Polymer and Web Components (Kostas Karolemeas) - GreeceJS #17The rise of Polymer and Web Components (Kostas Karolemeas) - GreeceJS #17
The rise of Polymer and Web Components (Kostas Karolemeas) - GreeceJS #17
GreeceJS
 
Challenges of angular in production (Tasos Bekos) - GreeceJS #17
Challenges of angular in production (Tasos Bekos) - GreeceJS #17Challenges of angular in production (Tasos Bekos) - GreeceJS #17
Challenges of angular in production (Tasos Bekos) - GreeceJS #17
GreeceJS
 
The case for HTTP/2
The case for HTTP/2The case for HTTP/2
The case for HTTP/2
GreeceJS
 
GraphQL vs REST
GraphQL vs RESTGraphQL vs REST
GraphQL vs REST
GreeceJS
 
Ellak JavaScript Day
Ellak JavaScript DayEllak JavaScript Day
Ellak JavaScript Day
GreeceJS
 
The history of asynchronous JavaScript
The history of asynchronous JavaScriptThe history of asynchronous JavaScript
The history of asynchronous JavaScript
GreeceJS
 
The tools & technologies behind Resin.io
The tools & technologies behind Resin.ioThe tools & technologies behind Resin.io
The tools & technologies behind Resin.io
GreeceJS
 

More from GreeceJS (19)

Introduction to react-query. A Redux alternative? (Nikos Kleidis, Front End D...
Introduction to react-query. A Redux alternative? (Nikos Kleidis, Front End D...Introduction to react-query. A Redux alternative? (Nikos Kleidis, Front End D...
Introduction to react-query. A Redux alternative? (Nikos Kleidis, Front End D...
 
Next.js and the pursuit of happiness (Dimitris Michalakos, Lead Developer at ...
Next.js and the pursuit of happiness (Dimitris Michalakos, Lead Developer at ...Next.js and the pursuit of happiness (Dimitris Michalakos, Lead Developer at ...
Next.js and the pursuit of happiness (Dimitris Michalakos, Lead Developer at ...
 
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...
An Emoji Introduction to React Native (Panagiotis Vourtsis, Senior Front End ...
 
Under the hood of RxJS (Dimitris Livas) - GreeceJS #27
Under the hood of RxJS (Dimitris Livas) - GreeceJS #27Under the hood of RxJS (Dimitris Livas) - GreeceJS #27
Under the hood of RxJS (Dimitris Livas) - GreeceJS #27
 
All About GRAND Stack: GraphQL, React, Apollo, and Neo4j (Mark Needham) - Gre...
All About GRAND Stack: GraphQL, React, Apollo, and Neo4j (Mark Needham) - Gre...All About GRAND Stack: GraphQL, React, Apollo, and Neo4j (Mark Needham) - Gre...
All About GRAND Stack: GraphQL, React, Apollo, and Neo4j (Mark Needham) - Gre...
 
Cross platform engineering - Lessons Learned (Michael Asimakopoulos, Valadis ...
Cross platform engineering - Lessons Learned (Michael Asimakopoulos, Valadis ...Cross platform engineering - Lessons Learned (Michael Asimakopoulos, Valadis ...
Cross platform engineering - Lessons Learned (Michael Asimakopoulos, Valadis ...
 
TypeScript: JavaScript that scales (Kostas Stergiou) - GreeceJS #22
TypeScript: JavaScript that scales (Kostas Stergiou) - GreeceJS #22TypeScript: JavaScript that scales (Kostas Stergiou) - GreeceJS #22
TypeScript: JavaScript that scales (Kostas Stergiou) - GreeceJS #22
 
Migrating from Monolithic to Serverless (Kostas Katsikas) - GreeceJS #22
Migrating from Monolithic to Serverless (Kostas Katsikas) - GreeceJS #22Migrating from Monolithic to Serverless (Kostas Katsikas) - GreeceJS #22
Migrating from Monolithic to Serverless (Kostas Katsikas) - GreeceJS #22
 
The JavaScript toolset for development on Ethereum
The JavaScript toolset for development on EthereumThe JavaScript toolset for development on Ethereum
The JavaScript toolset for development on Ethereum
 
Modern web development and accessibility (Christina Papadimitriou, Nadia Mark...
Modern web development and accessibility (Christina Papadimitriou, Nadia Mark...Modern web development and accessibility (Christina Papadimitriou, Nadia Mark...
Modern web development and accessibility (Christina Papadimitriou, Nadia Mark...
 
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
Cycle.js - Functional reactive UI framework (Nikos Kalogridis)
 
React Native and the future of web technology (Mark Wilcox) - GreeceJS #15
React Native and the future of web technology (Mark Wilcox) - GreeceJS #15React Native and the future of web technology (Mark Wilcox) - GreeceJS #15
React Native and the future of web technology (Mark Wilcox) - GreeceJS #15
 
The rise of Polymer and Web Components (Kostas Karolemeas) - GreeceJS #17
The rise of Polymer and Web Components (Kostas Karolemeas) - GreeceJS #17The rise of Polymer and Web Components (Kostas Karolemeas) - GreeceJS #17
The rise of Polymer and Web Components (Kostas Karolemeas) - GreeceJS #17
 
Challenges of angular in production (Tasos Bekos) - GreeceJS #17
Challenges of angular in production (Tasos Bekos) - GreeceJS #17Challenges of angular in production (Tasos Bekos) - GreeceJS #17
Challenges of angular in production (Tasos Bekos) - GreeceJS #17
 
The case for HTTP/2
The case for HTTP/2The case for HTTP/2
The case for HTTP/2
 
GraphQL vs REST
GraphQL vs RESTGraphQL vs REST
GraphQL vs REST
 
Ellak JavaScript Day
Ellak JavaScript DayEllak JavaScript Day
Ellak JavaScript Day
 
The history of asynchronous JavaScript
The history of asynchronous JavaScriptThe history of asynchronous JavaScript
The history of asynchronous JavaScript
 
The tools & technologies behind Resin.io
The tools & technologies behind Resin.ioThe tools & technologies behind Resin.io
The tools & technologies behind Resin.io
 

Recently uploaded

Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
UiPathCommunity
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
RTTS
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
Dorra BARTAGUIZ
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
Elena Simperl
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
Product School
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
DianaGray10
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
Alison B. Lowndes
 
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Product School
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
DianaGray10
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
Sri Ambati
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
Frank van Harmelen
 
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
Product School
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Inflectra
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
Safe Software
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
Jemma Hussein Allen
 

Recently uploaded (20)

Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
 
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
 

Taming forms with React

  • 1. REACT FORMS ... a crime scene ... CRIME SCENE - DO NOT CROSS
  • 2. mehiel@DOM React, since 2015 Logicea, since 2013 CanJS, since 2011 ExtJS, since 2010 jQuery, since 2008 Web, since 2004
  • 3. Forms: why bother? Inevitable Still a Pain Business Interest Fun?
  • 5. Forms: watch for the Designer the User the API the Browser
  • 6. Forms: ingredients Flexible Layout Variety of Inputs Dynamic Fields Dynamic Values Field Dependencies Lookup Data Validation Hints & Errors Field Status Requests & Responses
  • 7. React: the weapon Declarative Painless interactive UIs. Design views for each state in your app Predictable and easier to debug Component Based Encapsulated with own state Composable to make complex UIs Keep state out of the DOM
  • 8. // the simplest react component as of React 16 function WorldGreeter() { return "Hello, World" } <WorldGreeter /> => "Hello, World" // with some JSX sauce function FriendGreeter({ name }) { return <div>Hello {name}!</div> } <FriendGreeter name="Bob" /> => "Hello Bob!"
  • 9. class NobleGreeter extends React.Component { // componentLifecycleFunctions() // this.setState() render() { return ( <div> Hello {this.props.title} {this.props.name} </div> ) } } <NobleGreeter title="sir" name="William" /> => "Hello sir William"
  • 10. REACT FORMS a crime in 3 acts
  • 12. Act 1 : Scene 1 : ■ function ProfileForm1() { return ( <form> <input type="text" name="username" /> <input type="text" name="email" /> <input type="password" name="password" /> </form> ) }
  • 13. Act 1 : Scene 1 : ▸
  • 14. Act 1 : Scene 2 : ■ <div> <label htmlFor="username">Username</label> <input type="text" name="username" id="username" /> </div> <div> <label htmlFor="email">E-mail</label> <input type="text" name="email" id="email" /> </div> <div> <label htmlFor="password">Password</label> <input type="password" name="password" id="password" /> </div>
  • 15. Act 1 : Scene 2 : ▸ Username E-mail Password
  • 16. Act 1 : Scene 3 : ■ export class ProfileForm3 extends Component { onSubmit = (ev) => { ev.preventDefault() // disable browser post const data = { ... } fetch('/api/me', { method: 'POST', body: ... }) } inputs = {} render() { return (<form onSubmit={this.onSubmit}> ... </form>) } }
  • 17. Act 1 : Scene 3 : ■ inputs = {} onSubmit = (ev) => {... const data = { username: this.inputs.username.value, } } <form onSubmit={this.onSubmit}>... <input type="text" name="username" id="username" ref={(el) => this.inputs.username = el} /> </form>
  • 18. Act 1 : Scene 3 : ▸ Username E-mail Password Submit
  • 19. Act 1 : Scene 4 : ■ export class ProfileForm4 extends Component { onSubmit = (ev) => {... const data = {} new FormData(ev.target).forEach( (value, name) => data[name] = value ) ...} render() { ... <input type="text" name="username" id="username" /> } }
  • 20. Act 1 : Scene 4 : ▸ Username E-mail Password Submit
  • 21. Break : Controlled Input : ⚛ With controlled components react state is the “single source of truth”. <input type="text" value="GreeceJS" /> GreeceJS <input type="text" value={name} onChange={handleNameChange} />
  • 22. Break : Controlled Input : ⚛ feature uncontrolled controlled on submit value retrieval ✅ ✅ validating on submit ✅ ✅ instant eld validation ❌ ✅ conditionally disabling inputs ❌ ✅ enforcing input format ❌ ✅ dynamic inputs ❌ ✅
  • 23. Act 1 : Scene 5 : ■ export class ProfileForm5 extends Component { state = {} onUsernameChange = (ev) => { const value = ev.target.value this.setState(prevState => ({ ...prevState, username: value })) // async } onEmailChange = (ev) => { ... } onPasswordChange = (ev) => { ... } onSubmit = (ev) => { const data = this.state post('/api/me', data) } render() { ... } }
  • 24. Act 1 : Scene 5 : ■ render() { const { username = '', email = '', password = '' } = this.state // undefined values will mark inputs as uncontrolled // -> unintented use -> maybe bugs -> react warns return ( <form onSubmit={this.onSubmit}> <div> <label htmlFor="username">Username</label> <input type="text" name="username" id="username" value={username} onChange={this.onUsernameChange} /> </div> ... }
  • 25. Act 1 : Scene 5 : ▸ Username E-mail Password Submit
  • 26. Act 1 : Scene 6 : ■ export class ProfileForm6 extends Component { state = { username: '', email: '', password: '' } onSubmit = (ev) => {...} onChange = (ev) => { const { name, value } = ev.target this.setState(prevState => ({ ...prevState, [name]: value })) } render() { const { username, email, password } = this.state ... <input type="text" name="username" id="username" value={username} onChange={this.onChange} /> } }
  • 27. Act 1 : Scene 6 : ▸ Username E-mail Password Submit
  • 28. ACT 2 set the stage
  • 29. Break : Container Components : ⚛ You’ll nd your components much easier to reuse and reason about if you divide them into two categories. I call them Container and Presentational components - Dan Abramov
  • 30. Break : Container Components : ⚛ Concerned with how things work Provide the data and behavior Call ux actions and provide callbacks Often stateful, as they tend to serve as data sources Usually generated using higher order components
  • 31. Act 2 : Scene 1 : ■ export const ProfileContainer = Child => { class Container extends React.Component { state = { values: {}, errors: {} }; onSubmit = ev => post('/api/me', this.state.values) onChange = (values, errors) => { this.setState(prevState => ({ ...prevState, values, errors })); }; render() { const { children, ...childProps } = this.props; const { values, errors } = this.state; const { onChange, onSubmit } = this; return ( <Child {...childProps} form={{values, errors, onChange, onSubmit}}> {children}</Child> )}} return Container; }}
  • 32. Act 2 : Scene 1 : ■ class _ProfileForm7 extends Component { onChange = (ev) => { const { values, onChange } = this.props.form const { name, value } = ev.target const newValues = { ...values, [name]: value } onChange(newValues) } render() { const { values, onSubmit } = this.props.form return <form onSubmit={onSubmit}>... <input type="text" name="username" id="username" value={values.username || ''} onChange={this.onChange} /> }} const ProfileForm7 = ProfileContainer(_ProfileForm7) export default ProfileForm7
  • 33. Act 2 : Scene 1 : ▸ Username E-mail Password Submit
  • 34. Act 2 : Scene 2 : ■ <form onSubmit={onSubmit}> <div> <label htmlFor="username">Username</label> <input type="text" name="username" id="username" value={values.username || ''} onChange={this.onChange} /> </div> <div> <label htmlFor="email">E-mail</label> <input type="text" name="email" id="email" value={values.email || ''} onChange={this.onChange} /> </div> <div> <label htmlFor="password">Password</label> <input type="password" name="password" id="password" value={values.password || ''} onChange={this.onChange} /> </div> <input type="submit" /> </form>
  • 35. Act 2 : Scene 2 : ■ class Fields extends Component { ... }
  • 36. Act 2 : Scene 2 : ■ class _ProfileForm8 extends Component { schema = [{...}, {...}, {...}] render() { const { values, onChange, onSubmit } = this.props.form return ( <form onSubmit={onSubmit}> <Fields schema={this.schema} values={values} onChange={onChange} /> <input type="submit" /> </form> ) } } const ProfileForm8 = ProfileContainer(_ProfileForm8) export { ProfileForm8 }
  • 37. Act 2 : Scene 2 : ■ class Fields extends Component { onChangeHandler = ev => { const { values, onChange } = this.props; const { name, value } = ev.target; const newValues = {...values, [name]: (value || null) }; onChange(newValues); }; render() { const { schema, values } = this.props; return (<div>...</div>); } }
  • 38. Act 2 : Scene 2 : ■ schema = [{ path: "username", label: "Username", renderer: (path, value, label, onChange) => { return ( <div> <label htmlFor="username">Username</label> <input type="text" name="username" id="username" value={value} onChange={onChange} /> </div> ) } }]
  • 39. Act 2 : Scene 2 : ■ class Fields extends Component { onChangeHandler = ... render() { const { schema, values } = this.props; return (<div> {schema.map(field => field.renderer( field.path, values[field.path] || '', field.label, this.onChangeHandler) )} </div>); }}
  • 40. Act 2 : Scene 2 : ■ class _ProfileForm8 extends Component { schema = [{...}, {...}, {...}] render() { const { values, onChange, onSubmit } = this.props.form return ( <form onSubmit={onSubmit}> <Fields schema={this.schema} values={values} onChange={onChange} /> <input type="submit" /> </form> ) } } const ProfileForm8 = ProfileContainer(_ProfileForm8) export { ProfileForm8 }
  • 41. Act 2 : Scene 2 : ▸ Username E-mail Password Submit
  • 42. Act 2 : Scene 3 : ■ schema = [{ path: "username", label: "Username", renderer: (path, value, label, onChange) => { return ( <div> <label htmlFor="username">Username</label> <input type="text" name="username" id="username" value={value} onChange={onChange} /> </div> ) } }]
  • 43. Act 2 : Scene 3 : ■ const textRenderer = (path, value, label, onChange) => ( <div> <label htmlFor={path}>{label}</label> <input type="text" name={path} id={path} value={value} onChange={onChange} /> </div> ) schema = [ { path: "username", label: "Username", renderer: textRenderer } ]
  • 44. Act 2 : Scene 3 : ■ const textRenderer = (type = 'text') => (path, value, label, onChange) => ( <div> <label htmlFor={path}>{label}</label> <input type={type} name={path} id={path} value={value} onChange={onChange} /> </div> ) const passwordRenderer = () => textRenderer('password') schema = [ {path: "username", label: "Username", renderer: textRenderer()}, {path: "email", label: "E-mail", renderer: textRenderer()}, {path: "password", label: "Password", renderer: passwordRenderer()} ]
  • 45. Act 2 : Scene 3 : ■ class _ProfileForm9 extends Component { schema = [ { path: "username", label: ..., renderer: textRenderer() }, { path: "email", label: ..., renderer: textRenderer() }, { path: "password", label: ..., renderer: passwordRenderer() } ] render() { const { values, onChange, onSubmit } = this.props.form return ( <form onSubmit={onSubmit}> <Fields schema={this.schema} values={values} onChange={onChange} /> <input type="submit" /> </form> ) } } const ProfileForm9 = ProfileContainer(_ProfileForm9) export { ProfileForm9 }
  • 46. Act 2 : Scene 3 : ▸ Username E-mail Password Submit
  • 47. Act 2 : Scene 4 : ■ class _ProfileForm10 extends Component { schema = (values) => { const _schema = [ { path: "username", label: ..., renderer: textRenderer() }, { path: "email", label: ..., renderer: textRenderer() } ] if (values.email) _schema.push( { path: "password", label: ..., renderer: passwordRenderer() } ) return _schema; } render() { const { values, onChange, onSubmit } = this.props.form return ( ... <Fields schema={this.schema(values)} ... ) } }
  • 48. Act 2 : Scene 4 : ▸ Username E-mail Submit
  • 50. yeah, well! Not yet in the public but... yarn add @logicea/react-forms
  • 51. Act 3 : Scene 1 : ■ export const ProfileContainer = Child => { class Container extends React.Component { state = { values: {}, errors: {} }; onSubmit = ev => post('/api/me', this.state.values) onChange = (values, errors) => { this.setState(prevState => ({ ...prevState, values, errors })); }; render() { const { children, ...childProps } = this.props; const { values, errors } = this.state; const { onChange, onSubmit } = this; return ( <Child {...childProps} form={{values, errors, onChange, onSubmit}}> {children}</Child> )}} return Container; }}
  • 52. Act 3 : Scene 1 : ■ onChange = ev => { const { values, errors } = ev.detail this.setState(prevState => ({ ...prevState, values, errors })); };
  • 53. Act 3 : Scene 1 : ■ import Fields from '@logicea/react-forms/Fields' class _ProfileForm11 extends Component { schema = (values) => { const _schema = [ { row: 1, path: "username", ... }, { row: 2, path: "email", ... } ] if(...) _schema.push({ row: 3, path: "password", ... }) }) } render() { ... } }
  • 54. Act 3 : Scene 1 : ▸ Username E-mail Submit
  • 55. Act 3 : Scene 2 : ■ import Fields from '@logicea/react-forms/Fields' import yup from 'yup' class _ProfileForm12 extends Component { schema = [ { path: "username", validator: yup.string().required(), ... }, { path: "email", validator: yup.string().email(), ... }, { path: "password", validator: yup.string().min(4), ... }, ] render() { const { values, errors, onChange, onSubmit } = this.props.form return ... <Fields schema={this.schema(values)} values={values} errors={errors} onChange={onChange} /> } }
  • 56. Act 3 : Scene 2 : ■ const textRenderer = (type = 'text') => (path, value, label, onChange, error) => ( <div> <label>...</label> <input ... /> {error && <div style={{ color: 'red' }}>{error}</div>} </div> ) const passwordRenderer = () => textRenderer('password')
  • 57. Act 3 : Scene 2 : ▸ Username E-mail Password Submit
  • 58. Act 3 : Scene 3 : ■ class _ProfileForm13 extends Component { schema = (values) => { const _schema = [ ... { row: 4, path: "country", label: "Country", dependands: ["city"], renderer: textRenderer() }, { row: 5, path: "city", label: "City", renderer: textRenderer() }, ] return _schema; } render() { ... } }
  • 59. Act 3 : Scene 3 : ▸ Username E-mail Password Country City Submit
  • 60. Act 3 : Scene 4 : ■ const COUNTRIES = [ 'Greece', 'Burundi', 'Democratic Republic of Congo', 'Central African Republic' ] const CITIES = { 'Greece': ['Athens', 'Thessaloniki'], 'Burundi': ['Bujumbura', 'Muyinga'], 'Democratic Republic of Congo': ['Kinshasa', 'Lubumbashi'], 'Central African Republic': ['Bangui', 'Bimbo'] }
  • 61. Act 3 : Scene 4 : ■ const selectRenderer = (options = []) => (path, value, label, onChange, error) => ( <div> <label htmlFor={path}>{label}</label> <select name={path} id={path} value={value} onChange={onChange}> <option value=''></option> {options.map(o => <option value={o} key={o}>{o}</option>)} </select> {error && <div style={{ color: 'red' }}>{error}</div>} </div> ) }
  • 62. Act 3 : Scene 4 : ■ class _ProfileForm14 extends Component { schema = (values, allCountries, allCities) => { const cities = values.country ? allCities[values.country] : [] const _schema = [... { path: "country", renderer: selectRenderer(allCountries) ... { path: "city", renderer: selectRenderer(cities) ... ] return _schema; } render() { const schema = this.schema(values, COUNTRIES, CITIES) ... } }
  • 63. Act 3 : Scene 4 : ▸ Username E-mail Password Country City Submit
  • 64. Act 3 : Scene 5 : ■ const getCountries = (props) => { return new Promise(r => setTimeout(() => r(COUNTRIES), 200)) } const getCities = (props) => { const selectedCountry = props.form.values.country const citiesResponse = CITIES[selectedCountry] || [] return new Promise(r => setTimeout(() => r(citiesResponse), 200)) }
  • 65. Act 3 : Scene 5 : ■ class _ProfileForm15 extends Component { schema = (values, countries, countryCities) => [... { path: "country", renderer: selectRenderer(countries) ... { path: "city", renderer: selectRenderer(countryCities) ... ] render() {... const { countries, cities } = this.props.lookups const schema = this.schema(values, countries, cities) } } const ProfileForm15 = compose(ProfileContainer(), Lookups({ initial: ['countries'], resolver: (lookupField) => { ... }, rules: (oldProps, newProps) => { ... }, }))(_ProfileForm15) export { ProfileForm15 }
  • 66. Act 3 : Scene 5 : ■ const lookupsResolver = (lookupField) => { switch (lookupField) { // eslint-disable-line default-case case 'countries': return getCountries; case 'cities': return getCities; } } const lookupRules = (oldProps, newProps) => { let lookups = [] const countryChanged = oldProps.form.values.country !== newProps.form.values.country if (countryChanged) lookups.push('cities') return { lookups } }
  • 67. Act 3 : Scene 5 : ▸ Username E-mail Password Country City Submit
  • 68.
  • 69.
  • 70. Forms: other libs complex feature full bloated simple state mgmt redux-free our inspiration joi based not mature react-joi-forms
  • 74. Performance - Formik vs Redux Form