Create a FormBuilder component in React Native (Part 3)
Tags: react, reactnative, ios, javascript
Published: 2019-03-18 01:06:48

---
title: Create a FormBuilder component in React Native (Part 3)
published: true
description: 
tags: react, react-native, ios, javascript
cover_image: https://thepracticaldev.s3.amazonaws.com/i/68ghwpyr8mvgumn2edca.jpeg
series: Create a FormBuilder component in React Native
---

This series contents:
- [Part 1: Create a new React Native app](https://dev.to/dev_nope/create-a-formbuilder-component-in-react-native-intro--part-1-1m84)
- [Part 2: Create a simple Salary Calculator Form](https://dev.to/dev_nope/create-a-formbuilder-component-in-react-native-part-2-2dec)
- Part 3: Create custom form input and button components (current)
- [Part 4: Work on the `FormBuilder` component](https://dev.to/dev_nope/create-a-formbuilder-component-in-react-native-part-4-5gc3)
- [Part 5: Enable/disable form buttons on-the-fly](https://dev.to/dev_nope/create-a-formbuilder-component-in-react-native-part-5-535h)
- [Part 6: Create a Sign Up form](https://dev.to/dev_nope/create-a-formbuilder-component-in-react-native-part-6-2ohd)
- [Part 7: Add support for Boolean field type](https://dev.to/dev_nope/create-a-formbuilder-component-in-react-native-part-7-32c7)

# Part 3: Create custom form input and button components

When creating React Native apps you'll always run into situations where you want to use the same UI element in multiple places. Often that element will look and behave in a specific way. It's not really a good practice to customize a component over and over again in different places, so in order to avoid any code duplication, it's recommended to split your app screen into multiple little components, which can be re-used anywhere across your app later on if needed.

### Create a custom FormButton component

Let's take our form buttons as an example. We have two of them and they both:
- accept a title
- accept a callback function which is triggered when the button is tapped
- have the same styles.

We can easily define a custom `FormButton` component which will satisfy all these needs and will look like this:

```jsx
import React from 'react';
import PropTypes from 'prop-types';
import { TouchableOpacity, Text, StyleSheet } from 'react-native';

/**
 * A stateless function component which renders a button.
 *
 * @param {obj} props
 */
const FormButton = (props) => {
    const { children, onPress } = props;

    return (
        <TouchableOpacity style={styles.button} onPress={onPress}>
            <Text style={styles.buttonText}>{children}</Text>
        </TouchableOpacity>
    );
};

FormButton.propTypes = {
    onPress: PropTypes.func,
    children: PropTypes.string.isRequired,
};

FormButton.defaultProps = {
    onPress: f => f,
};

const styles = StyleSheet.create({
    button: {
        backgroundColor: '#FD6592',
        borderRadius: 3,
        height: 40,
        marginBottom: 15,
        justifyContent: 'center',
        alignItems: 'center',
    },
    buttonText: {
        color: '#FFF',
        fontWeight: 'bold',
        fontSize: 16,
    },
});

export default FormButton;
```

Here are some pinpoints I want to mention in regards to creating this new component:
- we've moved the button styles from our `App.js` file into this new component;
- we're using the [prop-types](https://www.npmjs.com/package/prop-types) library to document the intended types of properties passed to our component. For more information about how to use PropTypes, check out the [React's Typechecking With PropTypes](https://reactjs.org/docs/typechecking-with-proptypes.html) documentation. Please note that you're not required to use PropTypes while building your app. I just personally think it's a really good way to document your components and it can definitely be handy for both you or any other developer that'll have to work with your code sometime down the road. As you can see our component accepts two props: `onPress` and `children`. Because `onPress` is not a required prop, we need to define a default value for it, which in our case is a function that does "nothing" (or to be more specific, it's an arrow function with an implicit return);
- because our component doesn't do anything other than rendering something to the user, I've defined it as a stateless function instead of being `class` that extends `React.Component`. Again, this is just a preference. In fact, the other custom component we'll talk about in a second (`FormTextInput`) was defined as a `class`, just to demonstrate that both approaches can be used.

Now we can re-use this custom button anywhere in our app like so:
```jsx
// ...
import FormButton from 'path/to/FormButton';
// ...
render() {
    // ...
    return (
        <FormButton onPress={() => { console.log('Button pressed!'); }}>
            Press Me!
        </FormButton>
    );
}
```

### Create a custom FormTextInput component

The same way we created a custom component for our form buttons, we can create one for our form text inputs. That way if we'll have multiple screens with some forms that require text inputs, we can easily re-use our custom text inputs components. Let's create a new component and call it `FormTextInput`, which will look like this:

```jsx
import React from 'react';
import PropTypes from 'prop-types';
import {
    View, TextInput, Text, StyleSheet,
} from 'react-native';

/**
 * A component which renders a TextInput with a label above it.
 * Note: This component can easily be written as a stateless function
 *      since it only includes the `render()` function and nothing else
 *      (see FormButton component as an example).
 */
class FormTextInput extends React.Component {
    render() {
        const { labelText, ...inputProps } = this.props;

        return (
            <View style={styles.inputWrapper}>
                {labelText && <Text style={styles.label}>{labelText}</Text>}
                <TextInput style={styles.textInput} blurOnSubmit {...inputProps} />
            </View>
        );
    }
}

FormTextInput.propTypes = {
    labelText: PropTypes.string,
};

FormTextInput.defaultProps = {
    labelText: null,
};

const styles = StyleSheet.create({
    inputWrapper: {
        marginBottom: 15,
        flexDirection: 'column',
    },
    textInput: {
        height: 40,
        borderColor: '#FFF',
        borderWidth: 1,
        borderRadius: 3,
        backgroundColor: '#FFF',
        paddingHorizontal: 10,
        fontSize: 18,
        color: '#3F4EA5',
    },
    label: {
        color: '#FFF',
        marginBottom: 5,
    },
});

export default FormTextInput;
```

Here are some pinpoints I want to mention in regards to creating this new component:

- we've moved the text inputs styles from our `App.js` file into this new component;

- we're making use of JavaScript [rest](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters) and [spread](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) operators to collect  all default `TextInput`'s props (e.g. `placeholder`, `keyboardType`, etc.) we're passing down to the `FormTextInput` component, and spread them into the React Native's `TextInput` component. Pay attention to these 2 lines of code:
```jsx
// Using the "rest" operator.
// You can read the following line like this:
// Hey, give me the "labelText" property's value from "this.props" object 
// and store it into a "labelText" variable. Then, take all of the remaining
// properties from "this.props" and store them into a "inputProps" variable 
// (which ends up being an object).
const { labelText, ...inputProps } = this.props;

// Using the "spread" operator
// Now we can simply take our "inputProps" object which contains
// all of the props we wanted to pass down to the "TextInput" component,
// and spread them out into it.
// (same as writing: "<TextInput placeholder={inputProps.placeholder}" />
<TextInput style={styles.textInput} blurOnSubmit {...inputProps} />
```

- we're also using the `prop-types` library to document the intended types of properties passed to our component. Please note that we're not documenting any of the default `TextInput` component's props. That's because we don't really have control over them and React Native itself is taking care of that. The only prop we're documenting here is the `labelText`, which is an optional custom prop we're using to display a label above the text input. Check out the way we're checking in JSX if `labelText` has a value or not:
```jsx
{labelText && <Text style={styles.label}>{labelText}</Text>}
``` 
You can read this line like this: _if the labelText happens to be a "truthy" value then render out the Text component._

### Update our `App.js` file

Now it's the time to update our main `App.js` file to use these newly created components. The updated version should look like this:

```jsx
import React, { Component } from 'react';
import {
    StyleSheet, KeyboardAvoidingView, Text, Keyboard, Alert,
} from 'react-native';

import FormTextInput from './js/components/FormTextInput';
import FormButton from './js/components/FormButton';

export default class App extends Component {
    // ...
    render() {
        // ...
        return (
            <KeyboardAvoidingView behavior="padding" style={styles.container}>
                <Text style={styles.screenTitle}>Salary Calculator</Text>
                <FormTextInput
                    placeholder="$0"
                    keyboardType="numeric"
                    returnKeyType="done"
                    onChangeText={text => this.setState({ hourlyRate: text })}
                    value={hourlyRate}
                    labelText="Hourly Rate"
                />
                <FormTextInput
                    placeholder="0"
                    keyboardType="numeric"
                    returnKeyType="done"
                    onChangeText={text => this.setState({ hoursPerWeek: text })}
                    value={hoursPerWeek}
                />
                <FormButton onPress={this.handleSubmit}>Calculate</FormButton>
                <FormButton onPress={this.resetForm}>Reset</FormButton>
            </KeyboardAvoidingView>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        paddingHorizontal: 20,
        backgroundColor: '#3F4EA5',
    },
    screenTitle: {
        fontSize: 35,
        textAlign: 'center',
        margin: 10,
        color: '#FFF',
    },
});

```
For a full list of changes, check out this [commit on GitHub](https://github.com/vasilestefirta/react-native-form-builder/commit/f2cb16527f3d79a1aa903ee6d27acc44cc4a97e9).

---

Great job if you've made it this far 👏. Now it's the time to move to [Part 4](https://dev.to/dev_nope/create-a-formbuilder-component-in-react-native-part-4-5gc3), where we'll start working on a custom `FormBuilder` component which would render out a fully functional form. 👉