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

  • 691
Loading comments...

More from Gian Marco Toso