Full-Stack React & Node.js - Add a form
Tags: webdev, javascript, react, node
Published: 2022-06-27 15:44:38

---
series: Simply Learn Full-Stack React & Node.js
---

## Add a form to the React client site

We're going to add a few components here to generate our form from our data.  There are much better libraries to do this, which we will look at later, but for now we will write it ourselves.

Create all the following files under the **src** folder in our React project!

Create **Input.js** and paste in this code:

```jsx
import React, { useEffect, useRef } from "react";

const Input = ({
id, value = "", type = "text", readOnly = false, required = false
}) => {
  const input = useRef(null);

  useEffect(() => {
    if (input.current) {
      const sValue = value.toString();

      if (type === 'checkbox') {
        input.current.checked = sValue === 'true';
        input.current.value = 'true'
      } else {
        input.current.value = sValue
      }
    }
  }, [type, value])

  return (
    <input
      ref={input}
      id={id}
      name={id}
      type={type}
      readOnly={readOnly}
      disabled={readOnly}
      required={required}
    />
  );
};

export default Input;
```

> With `useEffect` and `useRef` hooks you can be certain that your uncontrolled inputs will update when the `value` prop changes.

**Input.js** creates text inputs or checkboxes depending on the data type of the *value* parameter. Next we need a component to render a label with an **Input.js**.

> React has two kinds of inputs: controlled and uncontrolled. With controlled the value is managed by React in `useState`. With uncontrolled the value is managed by the DOM. The difference is what the "single source of truth" is. Controlled are ideal when you have a small number of inputs. Uncontrolled perform better and, when your controls are inside a form, it's easier to have a single form event handler rather than an event handler on each input.

Create **InputLabel.js**, like this:

```jsx
import React from "react";
import Input from "./Input";

const InputLabel = ({label, error, info, ...inputProps}) => {
    return (
        <p
            className="input-label"
        >
            <label htmlFor={inputProps.id}>
                {
                    label
                }
            </label>
            <Input
                {...inputProps}
            />
        </p>
    );
};

export default InputLabel;
```

And now we make a form component with some string utility functions to turn an object into a bunch of form fields using our "Input" components.

Create **Form.js**:

```jsx
import React from 'react';
import InputLabel from "./InputLabel";
import './form.css'

const isNullOrUndefined = prop => prop === null
    || prop === undefined;
const isEmptyString = prop => isNullOrUndefined(prop)
    || prop === '';
const capitalize = word =>
    word.charAt(0).toUpperCase() +
    word.slice(1).toLowerCase();

function titleFromName(name) {
    if (isEmptyString(name)) {
        return '';
    }

    return name.split(/(?=[A-Z])|\s/).map(s => capitalize(s)).join(' ')
}

const Form = ({entity}) => {
  return (
    <form>
      {
        Object.entries(entity).map(([entityKey, entityValue]) => {
          if (entityKey === "id") {
            return <input
              type="hidden"
              name="id"
              key="id"
              value={entityValue}
            />
          } else {
            return <InputLabel
              id={entityKey}
              key={entityKey}
              label={titleFromName(entityKey)}
              type={
                typeof entityValue === "boolean"
                  ? "checkbox"
                  : "text"
                }
                value={entityValue}
              />
            }
          })
        }
      <button
        type="submit"
      >
        Submit
      </button>
    </form>
  );
};

export default Form;

```

And create **form.css**:

```css
form {
    padding: 1em;
    background: #f9f9f9;
    border: 1px solid #c1c1c1;
    margin: 2rem auto 0 auto;
    max-width: 600px;
}

form button[type=submit] {
    margin-left: 159px;
}

.input-label {
    display: flex;
}

.input-label label {
    font-weight: bold;
}

.input-label input {
    margin-left: 12px;
}

@media (min-width: 400px) {
    label {
        text-align: right;
        flex: 1;
    }

    input,
    button {
        flex: 3;
    }
}
```

Now change **AddEditNote.js** to use your **Form.js** component:

```jsx
import React from 'react';
import Form from './Form';

const noteEntity = {
    id: 1,
    title: 'A Note',
    content: 'Lorem ipsum dolor sit amet',
    author: 'neohed',
    lang: 'en',
    isLive: true,
    category: '',
}

const AddEditNote = () => {
    return (
        <div>
            <Form
                entity={noteEntity}
            />
        </div>
    );
};

export default AddEditNote;
```

To test this, inside the **node-react-stack/react-client** folder, run:

```shell
npm run start
```

You should see an HTML form with the values from the noteEntity object.

Now, to make it easier to see what data our app is using we will make a "debug" component. Create a new file, **RenderData.js**, like this:

```jsx
import React from 'react';
import './render-data.css'

const RenderData = ({data}) => {
    return (
        <div
            className='render-data'
        >
          <pre>
            {
                JSON.stringify(data, null, 3)
            }
          </pre>
        </div>
    );
};

export default RenderData;
```

Create **render-data.css**:

```css
@import url('https://fonts.googleapis.com/css2?family=Fira+Code&display=swap');

.render-data > pre {
    font-family: 'Fira Code', monospace;
    font-size: 1.2em;
    padding: 8px 0 0 32px;
}
```

> **Fira Code** is a nice monospace font provided by google. Monospace fonts are ideal for displaying code or data.

And finally, edit **AddEditNote.js**, like this:

```jsx
import React from 'react';
import RenderData from "./RenderData";
import Form from './Form';

const noteEntity = {
    id: 1,
    title: 'A Note',
    content: 'Lorem ipsum dolor sit amet',
    author: 'neohed',
    lang: 'en',
    isLive: true,
    category: '',
}

const AddEditNote = () => {
    return (
        <div>
            <RenderData
                data={noteEntity}
            />
            <Form
                entity={noteEntity}
            />
        </div>
    );
};

export default AddEditNote;
```

If you run the React app now, you should see a screen like this:


![App Screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v73elu1cr01nox71besj.jpg)

You could just `console.log` the `noteEntity` object, but sometimes it's easier to understand things when you use a component like this to render the object in the browser window.

Next we will [create the node.js server](https://dev.to/neohed/full-stack-react-nodejs-3-create-the-server-5a83)...

Code repo: [Github Repository](https://github.com/neohed/node-react-stack)