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:
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
anduseRef
hooks you can be certain that your uncontrolled inputs will update when thevalue
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:
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:
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:
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:
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:
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:
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:
@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:
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:
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...
Code repo: Github Repository
Top comments (0)