How to create custom forms with validation and scroll to invalid logic in React Native (Part one: Validation)
Tags: react, reactnative, mobile, javascript
Published: 2019-05-07 14:50:58

---
title: How to create custom forms with validation and scroll to invalid logic in React Native (Part one: Validation)
published: true
description: A tutorial on creating custom forms in React Native. Part one focuses on the validation logic.
tags: #react #reactnative #mobile #javascript
cover_image: https://thepracticaldev.s3.amazonaws.com/i/bv8l6czhojgqfik38c3p.png
series: How to create custom forms with validation and scroll to invalid logic in React Native
---

*Want to stay up-to-date? Check out React Native Now, the bi-weekly [React Native newsletter](https://reactnativenow.com/)*

---

When we [started our first rewrite](https://medium.com/lawnstarter-engineering/react-native-one-year-later-what-weve-learned-c23dc13cec80) into React Native in late 2017, we knew our app was going to have quite a few forms that our providers would need to fill out - especially when first onboarding onto our platform.

On the web, we were pretty familiar with a variety of the web APIs and libraries out there that offer form validation and an ability to scroll to the first invalid input-something that we insisted on having due to the existence of a few long forms - but with React Native there wasn't much available at the time, which is how we ended up rolling our own.

![scroll to invalid logic was crucial for this long form](https://thepracticaldev.s3.amazonaws.com/i/26pna4808y7ijhvij4kn.gif)

Over these next few posts, we'll go over how we built our logic, some of the caveats to be aware of in this approach, and finally some of the improvements that can be made upon it.

If you want to jump ahead and see the demo app that this tutorial created, you can do so [here](https://github.com/lawnstarter/react-native-form-helpers/tree/5bf9f1f27be1628927af1eea4753edb1265ec76a/demoApp).

## Validation

The first step will be creating a service so we can validate our inputs against a defined dictionary of validation logic. For this, we first need to install [Validate.js](https://validatejs.org/) as a helper library. The nice thing about this library is that it's a pure Javascript solution, so there's no need to worry about any framework-specific constraints - so if you eventually want to port your validation over to the web or a different mobile framework, you can.

The first step in setting up Validate.js is to create a dictionary of all the different unique types of input fields you'll have and the validation rules for each. Reading though the [documentation](https://validatejs.org/#validators) on this will definitely help - but here's an example of some of our rules to give you some context:

```js
export const validationDictionary = {
  bool: {
    presence: {
      allowEmpty: false,
      message: "^This is required"
    }
  },

  day: {
    presence: {
      allowEmpty: false,
      message: "^This is required"
    },
    numericality: {
      greaterThan: 0,
      lessThanOrEqualTo: 31,
      message: "^Must be valid"
    }
  },

  email: {
    presence: {
      allowEmpty: false,
      message: "^This is required"
    },
    email: {
      message: "^Email address must be valid"
    }
  },
};
```

[Now that we have our dictionary setup](https://github.com/lawnstarter/react-native-form-helpers/blob/5bf9f1f27be1628927af1eea4753edb1265ec76a/demoApp/validation/dictionary.js), let's look at what the local state on one of our forms will look like.

```js
this.state = {
  inputs: {
    first_name: {
      type: "generic",
      value: ""
    },
    last_name: {
      type: "generic",
      value: ""
    },
    birthday_month: {
      type: "month",
      value: ""
    },
    birthday_day: {
      type: "day",
      value: ""
    }
  }
};
```

As you can see, we've nested all of our `inputs` within an inputs object on our state to keep it organized. Then, each individual input has its own object with two key pieces nested within it. First is the `type`, which is a string representation linked to the type of validation we want to perform on the input. Next is the `value`, which is where the input's value will be stored. Also note that the key of each input is uniquely tied to the input in reference.

Next, let's create some helper methods that we'll utilize to validate our inputs. For ease of explaining how these helper methods will work - imagine we're validating an input defined as `day` which permits integers from 1 to 31 and the user has just typed 32 into the input.

```js
function validateInput({ type, value }) {
  const result = validatejs(
    {
      [type]: value
    },
    {
      [type]: validationDictionary[type]
    }
  );

  if (result) {
    return result[type][0];
  }

  return null;
}
```

First, we'll setup the main helper that's going to be validating our input called `validateInput`. This method will take two parameters, `type` ('day') and `value` (32). Within this method, we immediately call Validate.js with two parameters - the first is an object of our `type` and `value`, and the second is an object of the validation we want to check against from our dictionary (make sure your `validationDictionary` exists for this helper to reference). If that call produces a result - which in this case it would because our dictionary enforces numbers between 1 and 31, the error of `"Must be valid"` will be returned.

```js
function getInputValidationState({ input, value }) {
  return {
    ...input,
    value,
    errorLabel: input.optional
      ? null
      : validateInput({ type: input.type, value })
  };
}
```

Next, we'll create a helper method called `getInputValidationState`. This method will be updating the `state.inputs.day` object by first spreading out all of the existing parameters, and then explicitly updating the `errorLabel` parameter (as long as the input hasn't been flagged as optional) by calling the `validateInput` method we just created.

```js
function onInputChange({ id, value, cb = () => {} }) {
  const { inputs } = this.state;
  this.setState(
    {
      inputs: {
        ...inputs,
        [id]: getInputValidationState({
          input: inputs[id],
          value
        })
      }
    },
    cb
  );
}
```

Finally, we need a helper method that we'll link directly into the change event on our input (i.e the `onChangeText` prop on a `TextInput`). This method will not only update state.inputs.day.value, but also actually perform the validation and return all information in regards to it. To do this, we'll create an `onInputChange` helper that will take three parameters. First, an id that is a string representation of the input's unique key - `day` in this instance. Next, the `value` (32) of the input. And finally, an optional callback method. We will need to bind this method to our constructor so it has access to state, which will allow us to pull the entire object for that specific input off.

```js
this.onInputChange = validationService.onInputChange.bind(this);
```

[Now that we've got our helpers setup](https://github.com/lawnstarter/react-native-form-helpers/blob/5bf9f1f27be1628927af1eea4753edb1265ec76a/demoApp/validation/service.js), let's go back to the setup of our form. The beauty of the service we just created is that it can validate any structure of data as long as your dictionary is setup to parse it (again, refer to the [documentation](https://validatejs.org/#validators) for more assistance with complex rules). But for the sake of simplicity, I'll validate a few `TextInput` components using our `onInputChange` method.

```js
  renderError(id) {
    const { inputs } = this.state;
    if (inputs[id].errorLabel) {
      return <Text style={styles.error}>{inputs[id].errorLabel}</Text>;
    }
    return null;
  }

  render() {
    return (
      <View style={styles.container}>
        <ScrollView>
          <View>
            <Text>First Name</Text>
            <TextInput
              style={styles.input}
              onChangeText={value => {
                this.onInputChange({ id: "first_name", value });
              }}
            />
            {this.renderError("first_name")}
          </View>
```

Here's a snippet of what our form looks like when it's all set up. Essentially, we've got four inputs that all have validation checks, and a helpful `renderError` method that looks to see if the `errorLabel` key on any of our individual inputs is truthy. If it is, we render the error.

![](https://thepracticaldev.s3.amazonaws.com/i/2ucelecfzh4aaqjlicp5.png)

Great, now that we have individual inputs validating -but  what about if we wanted to validate the entire form all at once? Let's create a new helper method called `getFormValidation` to do that.

```js
function getFormValidation() {
  const { inputs } = this.state;

  const updatedInputs = {};

  for (const [key, input] of Object.entries(inputs)) {
    updatedInputs[key] = getInputValidationState({
      input,
      value: input.value
    });
  }

  this.setState({
    inputs: updatedInputs
  });
}
```

For this helper method, we're going to iterate over the entire `inputs` object and run a validation check on each input. As we run this validation, we rebuild the `inputs` object and then set it to state. Make sure you bind this one to the constructor too, then throw a button on your form and test calling this method.

![](https://thepracticaldev.s3.amazonaws.com/i/wpxwnzl2adwybxn500n9.gif)

Now [our form is setup](https://github.com/lawnstarter/react-native-form-helpers/blob/5bf9f1f27be1628927af1eea4753edb1265ec76a/demoApp/App.js) with our base validation logic.

##Next Steps
That's all for this first part. In part two, we'll go over setting up "scroll to invalid" logic so we can ensure that if we have a long form and our user forgets to enter in a field on the top input - we bring them right to that point on the form.