I am trying to create a form to capture values. My data will come in looking like this:
const dummyData = [
{ question: "what is your name?", isActive: true },
{ question: "what is your age?", isActive: true },
{ question: "what is your dob?", isActive: true }
];
I need to get values of all the inputs for the above questions.
I am trying to set formData
to a state
that looks like this
const formData = {
whatIsYourName: '',
whatIsYourAge: '',
whatIsYourDob?: ''
};
import "./styles.css";
import { useState, useEffect } from "react";
const dummyData = [
{ question: "what is your name?", isActive: true },
{ question: "what is your age?", isActive: true },
{ question: "what is your dob?", isActive: true }
];
const App = () => {
const [formData, setFormData] = useState({});
const converDataToObject = () => {
//do something
};
useEffect(() => {
converDataToObject();
}, []);
return <div className="App">test</div>;
};
export default App;
CodePudding user response:
Try to use this helper function to generate the desider object:
const dummyData = [
{ question: "what is your name?", isActive: true },
{ question: "what is your age?", isActive: true },
{ question: "what is your dob?", isActive: true }
];
const converDataToObject = () => {
const result = {}
for (const obj of dummyData) {
result[generateKey(obj.question)] = ""
}
return result;
};
// Generate a key from the given question
const generateKey = (question) => {
// Split the question by ` ` after removing the `?`
const keySplit = question.toString().replaceAll("?", "").split(" ");
// Capitalize each substring after the first one
// and join the result into a single value
return keySplit.map((s, idx) => {
if (idx === 0) return s;
return capitalize(s);
}).join("");
}
// Helper function, capitalize a string
const capitalize = (s) => {
return s.charAt(0).toUpperCase() s.slice(1).toLowerCase();
}
console.log(converDataToObject())
You will just need to set the formData
with:
setFormData(convertDataToObject())
CodePudding user response:
You may want to keep everything together in state.
whatisyourname: { question: 'What is your name?', isActive: true }
and then add a new value
property to the state when a corresponding input
is updated.
That said it would be better (if you have control over the incoming data) to add a name
property to the object, and use that instead, since it's much more meaningful.
{ name: 'name', question: 'What is your name?', isActive: true },
becomes this when it's converted, and you don't have to do any unnecessary find/replace on the key name.
name: { name: 'name', question: 'What is your name?', isActive: true },
const { useEffect, useState } = React;
// Small conversion function that
// creates an object with nested objects
function convertData(data) {
const out = {};
for (const obj of data) {
const key = obj.question
.toLowerCase()
.replaceAll(/[ ?] /g, '');
out[key] = obj;
}
return out;
}
function Example({ data }) {
const [ form, setForm ] = useState({});
// Create the state using the conversion function
useEffect(() => {
const converted = convertData(data);
setForm(converted);
}, []);
// A function to create the form
// It maps over each property, and creates
// the labels and inputs
function createForm(form) {
return Object.entries(form).map(([key, obj]) => {
return (
<div className="input">
<label>{obj.question}</label>
<input
key={key}
name={key}
value={obj.value}
/>
</div>
);
});
}
// A function to handle changes to any
// of the input values. Because we're using event
// delegation we first check that the element that was
// changed was an input, then we extract the name and value
// from it, and set the new form, updating the value in the object
function handleChange(e) {
if (e.target.matches('input')) {
const { name, value } = e.target;
setForm({
...form,
[name]: { ...form[name], value }
});
}
}
useEffect(() => console.log(form), [form]);
// Use event delegation on the div
// to check for changes in the child elements
return (
<div onChange={handleChange}>
{createForm(form)}
</div>
);
}
const data = [
{ question: 'What is your name?', isActive: true },
{ question: 'What is your age?', isActive: true },
{ question: 'What is your dob?', isActive: true }
];
ReactDOM.render(
<Example data={data} />,
document.getElementById('react')
);
label { margin-right: 1em; }
.input { margin-bottom: 0.5em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Additional documentation