Forms

Forms are bread and butter of any app. Textual, numerical, date and other kinds of input are typically placed to capture user input.

render() {
    return <input type="text" />
}

How do we capture the input when submitting forms or on some other action? Typical solution is to add an id to the control and use document.getElementById to find the control and extract the value. But that is not the way of React!

What we want is to save the result of the value in our state. First, let's prepare a backing field in our state:

interface State {
    name: string
}

class Form extends React.Component<{}, State> {
    state = {
        name: ""
    }
}

Now use that value in our JSX:

render() {
    return <input type="text" value={this.state.name} />
}

Try running this code and see what happens. The results are surprising - the input is read only and we cannot type in it! Since there is no two-way binding, even though we type, nothing is saved in our state and our control keeps rendering the current value of our state!

To fix that, let's handle changes and update the corresponding jsx.

render() {
    return <input type="text"
                  value={this.state.name}
                  onChange={e => this.setState({ name: e.target.value })}
                  />
}

Now run the code and notice that it works as expected.

Move inline code to methods

We don't want to inline our code all the time to keep our JSX small and fresh so we will refactor the above code:

handleNameChange = (e: any) => {
    this.setState({ name: e.target.value })
}
render() {
    return <input type="text"
                  value={this.state.name}
                  onChange={this.handleNameChange}
                  />
}

Repetition

If our form contains multiple inputs, the code might start to get a little bit repetitive:

handleNameChange = (e: any) => {
    this.setState({ name: e.target.value })
}
handleLastNameChange = (e: any) => {
    this.setState({ lastName: e.target.value })
}
handleCompanyNameChange = (e: any) => {
    this.setState({ companyName: e.target.value })
}
// ...it goes on and on and on

To handle repetition, we can use one cool javascript trick. First add a name to each input and then write a generic set method:

set = (e: any) => {
    this.setState({ [e.target.name]: e.target.value })
}
render() {
    return <input type="text"
                  name="name"
                  value={this.state.name}
                  onChange={this.set}
                  />
}

Non-text inputs

In case we want numbers, it is preferred to write something like:

handleNameChange = (e: any) => {
    // parseInt ensures that the value is a number and not a string
    this.setState({ name: parseInt(e.target.value) })
}

In case checkbox is used, never use e.target.value, instead use e.target.checked

handleWantMoneyChange = (e: any) => {
    // checked gives a boolean here
    this.setState({ wantMoney: e.target.checked })
}

Conclusion

Since React only supports one-way binding from state to the UI, all changes must go through manual handlers. Even though this seems quite repetitive at first, it can be simplified as shown above. This approach also forces explicit validation and coercion rules that are part of the code without resorting to some other means that two-way binding frameworks typically use.

results matching ""

    No results matching ""