Skip to content

Instantly share code, notes, and snippets.

@philnash
Last active January 28, 2022 04:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save philnash/0740b7b30557cca72a093a4db402527f to your computer and use it in GitHub Desktop.
Save philnash/0740b7b30557cca72a093a4db402527f to your computer and use it in GitHub Desktop.
Dynamic React form based on object of data.

A Dynamic React form

These are React components that can render a form with controlled components based on an arbitrary object of data passed in as the only prop.

I'm interested in whether this was the right approach or if I'm missing something similar. Any feedback is welcome!

import "./App.css";
import Form from "./Form";
function App() {
const startingData = {
name: "Phil",
jobs: [
{ title: "Front end developer", company: "ACME Dev" },
{ title: "Full stack developer", company: "Another place" },
{
title: "Developer evangelist",
company: "Twilio",
more: [{ fun: "always" }, { otherThoughts: "This is cool" }],
},
],
};
return (
<div className="App">
<header className="App-header">
<h1>Dynamic Form</h1>
</header>
<main>
<Form startingData={startingData}></Form>
</main>
</div>
);
}
export default App;
import React, { useState } from "react";
import FormRow from "./FormRow";
function Form({ startingData }) {
const [data, setData] = useState(startingData);
function updateObject(obj, keys, value) {
// deep copy the state object
obj = JSON.parse(JSON.stringify(obj));
let innerObj = obj;
// Follow the keys to the value that needs to be updated
for (var i = 0; i < keys.length - 1; i++) {
innerObj = innerObj[keys[i]];
}
// Update the value
innerObj[keys[i]] = value;
// Return the new state object
return obj;
}
const handleUpdate = (event) => {
// Find the keys of the object that lead to the value that needs to be changed
const path = event.target.name;
const keys = path.split(".");
// Update the state
setData(updateObject(data, keys, event.target.value));
};
const handleSubmit = (event) => {
event.preventDefault();
console.log("Data submitted: ", data);
};
return (
<form onSubmit={handleSubmit}>
{Object.keys(data).map((key, i) => (
<FormRow
key={`${key}-${i}`}
path={key}
value={data[key]}
handleUpdate={handleUpdate}
label={key}
/>
))}
<button type="submit">Save</button>
</form>
);
}
export default Form;
import React from "react";
function FormRow({ path, value, handleUpdate, label }) {
if (typeof value === "string") {
// If the value is a string, render a label and input
return (
<div key={path}>
<label>{label}</label>
<input
type="text"
value={value}
onChange={handleUpdate}
name={path}
></input>
</div>
);
} else {
// Otherwise, assume the value is an array and render a new FormRow for each
// item in the array
return value.map((obj, i) => {
return (
<div
style={{
marginLeft: "10px",
borderLeft: "2px solid #000",
}}
key={`${path}-${i}`}
>
{Object.keys(obj).map((key) => (
<FormRow
key={`${path}.${i}.${key}`}
// We construct a path here that is a map of keys/indices to the
// value from the root of the original object
path={`${path}.${i}.${key}`}
value={obj[key]}
handleUpdate={handleUpdate}
label={key}
/>
))}
</div>
);
});
}
}
export default FormRow;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment