Easier Forms with Render Props

A shameless story of self promotion

Gian Marco Toso

@gianmarcotoso

gianmarcotoso

gianmarcotoso.com

polarityb.it

Software Engineer, The Pirate I Was Meant To Be

šŸŽ© About Me šŸŽ©

Born and raised in Turin, Italy

@gianmarcotoso

gianmarcotoso

gianmarcotoso.com

polarityb.it

MSc in Computer EngineeringĀ 

Self Employed Software Engineer

Javascript and PHP Developer

šŸ” About My Stack šŸ”

Docker

PHP/Laravel

"It was working on my machine!"

The monolith is here to stay

NodeJS/TypeScript

Seriously, it's awesome!

React + Redux

The Flux Capacitor!

A Form in React

import React from 'react'

class MyForm extends React.Component {
    state = {
        name: '',
        password: ''
    }

    handleFormValueChange = 
        (ev) => this.setState({[ev.target.name]: ev.target.value})

    render() {
        return (
            <div>
                <input 
                    name="name" 
                    onChange={this.handleFormValueChange} 
                    value={this.state.name} 
                />
                <input 
                    name="password" 
                    onChange={this.handleFormValueChange} 
                    value={this.state.password}
                    type="password" 
                />
            </div>
        )
    }
}

A Form in React

We need to manage the state manually

We actually needĀ a state

Forms cannot be stateless components

The code is almost always the same

šŸ˜¢

Packages!

Render Props

A render prop is a functionĀ that is called by the component it's passed to with some argumentsĀ and returns a valid ReactNode element

Any prop can be a render prop...

... even `children`!

Render Props

import React from 'react'

class MyContainer extends React.Component {
    render() {
        return (
            <Capitalizer name="billy">
                {(capitalizedName) => (
                    <h1>Hello, {capitalizedName}!</h1>
                )}
            </Capitalizer>
        )
    }
}

// Hello, Billy!

The point of using render props is to have components that do some kind of elaboration but leave it to us to decide whatĀ to render with the data they produce

Render Props

import React from 'react'

class Capitalizer extends React.Component {
    render() {
        const { name } = this.props
        const capitalizedName = name[0].toUpperCase + name.substr(1)

        // We are CALLING the children prop!
        return this.props.children(capitalizedName)
    }
}

In this example, Capitalizer takes a `name` prop and capitalizes the first character. It knows nothingĀ about rendering, because rendering is delegatedĀ to its parent component

Form + Render Props

Can we use this approach to simplify how we write forms?

Yes!

Ā 

(and I made a package that I'll shamelessly promote later...)

Form + Render Props

We want to be able to write this

import React from 'react'

const MyForm = ({onSubmit}) => (
    <FormHandler initial={{name: '', password: ''}}>
        {(data, onChange) => (
            <div>
                <input 
                    name="name" 
                    value={data.name} 
                    onChange={onChange} 
                />
                <input 
                    name="password" 
                    value={data.password} 
                    onChange={onChange} 
                />
                <button 
                    type="submit"
                    onClick={() => onSubmit(data)}
                />
            </div>
        )}
    </FormHandler>
)

Form + Render Props

FormHandler acts as a minimal state container, updating its internal state every time ourĀ data changes, and passing these changes back to us so that we can render them

Form + Render Props

import React from 'react'

class FormHandler extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            data: props.initial
        }
    } 

    onHandleDataChange = 
        (ev) => this.setState(state => {
            return {
                data: {
                    ...state.data,
                    [ev.target.name]: ev.target.value
                }
            }
        })

    render() {
        return this.props.children(this.state.data, this.onHandleDataChange)
    }
}

It's just thatĀ easy!

I made a package, react-attire, that does this and handles a few other cases, supporting validation and data transformation as well

It also works with React Native ;)

Check it out!

Form Validation with JOI

JSON Form Builder

Thank you! :)

Easier Forms with Render Props

By Gian Marco Toso

Easier Forms with Render Props

Slides for my second talk at the 2017 WebAppConf in Torino

  • 2,036