I am creating a sample dynamic form and I want to load my input elements based on a JSON which contains different input elements like "textbox", "text-area", "dropdown", "radio-input" and so on.. I have a JSON file created to get this as shown below:
[
{
"id": "1",
"type": "textbox",
"text": "",
"required": true,
"label": "lbl"
},
{
"id": "2",
"type": "textbox",
"text": "",
"required": true,
"label": "Specification Name"
},
{
"id": "3",
"type": "dropdown",
"text": "",
"required": true,
"label": "Specification Reviewed",
"options":["a","2"]
},
{
"id": "4",
"type": "dropdown",
"text": "",
"required": true,
"label": "Action Required",
"options":["1","2","3"]
},
{
"id": "5",
"type": "textbox",
"text": "",
"required": true,
"label": "lbl"
}
]
I have an App base component which calls another component called "Input" which has my layout and I retrieve the elements through that component. I am able to pull the text box and dropdown here but I am not able to iterate through the dropdown select. I'm not sure how to do it.
Here's my App Base solution: Here I use the map concept to fetch the data from the JSON local file and assign it to inputvalues which I then use in the return within the form tag.
- I'm able to list all my input elements dynamically
- But I'm not able to get the dropdown values from my JSON file
function App() {
const [inputObject, setInputObject] = React.useState(inputData)
const inputvalues = inputObject.map( input => {
return (
<Input
key={input.id}
input={input}
/>
)
})
const handleSubmit = (event) => {
event.preventDefault();
}
return (
<div className="App">
<header className="App-header">
<form>
<div>
{inputvalues}
</div>
<input type="submit" value="submit" onClick={handleSubmit} />
</form>
</header>
</div>
);
}
export default App;
And, here's my input.js component file: This basically lays out the input elements and I fetch the data using Props but I am unable to fetch the dropdown selection values because I would need to somehow iterate within each of those dropdown elements.
export default function Input(props) {
const [state, setState] = React.useState({
textBoxValue: ""
})
function handleChange(evt) {
setState({ [props.input.id] : evt.target.value });
}
if (props.onChange) {
props.onChange(state);
}
return (
<div>
<label>{props.input.type}: </label>
{props.input.type === "textbox" && <input name={props.input.type} placeholder={props.input.type} id={props.input.id} value={state.firstName} onChange={handleChange}/>}
{props.input.type === "dropdown" && <select name={props.input.type} id={props.input.id}>
<option value={props.input.options}></option></select>
}</div>)}
Please help me or guide me because I'm still learning React. In addition to this, how would i later get all the input values upon FORM SUBMIT ? For this I tried adding a handleChange event to see if data comes through but it does not work.
Thank you so much in advance!
CodePudding user response:
You may find Yup and Formik useful.
With Yup, you can include types to fields as well as things such as if the field is required.
The example linked should get you in the right direction.
Edit - (after OP comment)
So without using any external library, you could do something like this:
// Get a hook function
const {useState} = React;
const INPUTS = [
{
"id": "1",
"type": "textbox",
"value": "",
"required": true,
"label": "lbl"
},
{
"id": "2",
"type": "textbox",
"value": "",
"required": true,
"label": "Specification Name"
},
{
"id": "3",
"type": "dropdown",
"value": "",
"required": true,
"label": "Specification Reviewed",
"options":["a","2"]
},
{
"id": "4",
"type": "dropdown",
"value": "",
"required": true,
"label": "Action Required",
"options":["1","2","3"]
},
{
"id": "5",
"type": "textbox",
"value": "",
"required": true,
"label": "lbl"
}
];
const convertArrayToObject = (array, key, targetKey) => {
const initialValue = {};
return array.reduce((obj, item) => {
return {
...obj,
[item[key]]: item[targetKey],
};
}, initialValue);
};
const Form = () => {
const [formState, setFormState] = useState(
convertArrayToObject(INPUTS, "id", "value")
);
const handleChanges = (keyName) => (e) => {
const newValue = e.target.value
setFormState(prev => ({
...prev,
[keyName]: newValue
}));
}
console.log(formState);
return (
<form>
{INPUTS.map((input, inputIndex) => (
<div key={inputIndex}>
<label htmlFor={input.id}>{input.label}</label>
{input.type === "dropdown" && input.options ? (
<select onChange={handleChanges(input.id)}>
{input.options.map((option, optionIndex) => (
<option
key={optionIndex}
value={option}>{option}
</option>
))}
</select>
) : (
<input
id={input.id}
name={input.id}
required={input.required}
type={input.type}
onChange={handleChanges(input.id)}
value={formState.value}
/>
)}
</div>
))}
</form>
);
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Form />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
A little reasoning behind some of the code written:
- React wants a
key
prop to be passed when mapping over objects (hence I've added it for each wrapperdiv
andoption
element. - I've mapped over the
INPUTS
object to build the initial state, and then created anonChange
handler that is curried, that way it is generic enough to be used everywhere - I'm just
console.log
ing theformState
to demonstrate the changes as you update the form.
Beyond
- Consider adding Auto Complete if applicable or worthwhile
You will of course need some kind of some kind of submit button if you plan to submit the data to an API.
<button type="submit">Submit</button
But I will leave that as an exercise for you...
Hope this helps!