I want to create a react form with dynamic inputs, those inputs are fetched from server.
I fetched the data and I have displayed the form but the problem was how to get the values of the inputs with the onChange method
Use a Usestate hook and assign the value fetched from the API there
eg:-
const [value,setValue] = useState(fetch());
function fetch(){
//API call...
//return the value got from the API
}
Now in form input field you can use this as value and have an onChange and change the value state.
eg:-
<input value={value.name} name="name" onChange={(e)=>handlechange(e)} />
in handlechange :
const handlechange = (e)=>{
const {name,value} = e.target;
setValue({...value,[name]:value});
};
After this you can have an useEffect, which will POST the value to the backend using another API for every changes made in the form
Here is the code sandbox link:https://codesandbox.io/s/blazing-moon-wxp4w7?file=/src/App.js
after some researches on stackoverflow here is my solution:
const [fields, setFields] = useState([{field:"", value:""}]);
useEffect(()=>{
if(fetchedData){
setFields(fetchedData.map((item)=>{
var rObj ={field:"", value:""};
rObj["field"] = item;
return rObj;
}));
}
},[fetchedData])
const handleChange = ( index, event ) => {
if(fields) {
let data = [...fields];
data[index]["value"] = event.target.value;
setFields(data);
}
}
/////////////////////////////////////////
{fetchedData.map((field, index) => (
<div key={index}>
<input
required
type="text"
name={field}
value={fields.field}
placeholder= {field}
onChange={event=>{handleChange(index, event)}}
/>
</div>
))}
Here is a sample code:
const GenerateForm() => {
const [formInputs, setFormInputs] = useState({});
const handleInputChange = event => {
const { name, value } = event.target;
setFormInputs({ ...formInputs, [name]: value });
};
return (
<form>
{formData.map(input => {
if (input.type === "text" || input.type === "number") {
return (
<div key={input.name}>
<label htmlFor={input.name}>{input.label}:</label>
<input
type={input.type}
name={input.name}
value={formInputs[input.name] || ""}
onChange={handleInputChange}
/>
</div>
);
} else if (input.type === "select" || input.type === "radio") {
return (
<div key={input.name}>
<label>{input.label}:</label>
{input.options.map(option => (
<div key={option.value}>
<input
type={input.type}
name={input.name}
value={option.value}
checked={formInputs[input.name] === option.value}
onChange={handleInputChange}
/>
{option.label}
</div>
))}
</div>
);
}
return null;
})}
</form>
);
}
const formData = [
{
name: "firstName",
label: "First Name",
type: "text"
},
{
name: "lastName",
label: "Last Name",
type: "text"
},
{
name: "age",
label: "Age",
type: "number"
},
{
name: "gender",
label: "Gender",
type: "radio",
options: [
{
value: "male",
label: "Male"
},
{
value: "female",
label: "Female"
}
]
},
{
name: "country",
label: "Country",
type: "select",
options: [
{
value: "india",
label: "India"
},
{
value: "usa",
label: "USA"
}
]
}
];
Related
On selecting first dropdown then the second dropdown should show corresponding phonenumber in second dropdownlist and vice versa .how can we achieve it?
Code
import Select from "react-select";
const data = [
{ value: 1, label: "max", phone: "123" },
{ value: 2, label: "sam", phone: "345" },
{ value: 3, label: "denis", phone: "4444" }
];
export default function App(props) {
const [select1, setSelect1] = useState([]);
const [select2, setSelect2] = useState([]);
const filter1=data.filter((x)=>{return x.value===select2.value})
const filter2=data.filter((x)=>{return x.value===select1.value})
return (
<div className="App">
<Select
options={data}
value={select1}
onChange={(e) => {
console.log(e);
setSelect1(e);
setSelect2(e.phone);
}}
/>
<Select
options={data}
value={select2}
onChange={(e) => {
setSelect2(e);
setSelect1(e.value);
}}
/>
</div>
);
}
I am trying to grab the user input on key pressed and pass it to that list above next. I feel like there must be a way to reset the state and make it persist, but I just can't figure it out? How can I understand this?
import { useState, useEffect, useRef, useMemo } from 'react';
import '../sign.css';
const VALUES = [
{ id: 1, label: "name", text: "Hi, What is your Name?", placeholder: "Enter your full name" },
{ id: 2, label: "uname", text: "What shall we call you?", placeholder: "Enter a username" },
{ id: 3, label: "email", text: "Enter you email", placeholder: "Email" },
{ id: 4, label: "password", text: "Choose a password", placeholder: "make sure you dont forget" },
{ id: 5, label: "signup", text: "sign up", placeholder: ""},
];
export default function SignUp() {
const [show, setShow] = useState(VALUES)
const [currentIndex, setCurrentIndex] = useState(0);
const [details, setDetails] = useState('');
useEffect(() => {
}, [show]);
const onKeyPressed = (ev, id) => {
if (ev.charCode === 13) {
ev.preventDefault();
const nextRender = currentIndex + 1;
if (nextRender < show.length) {
setCurrentIndex(nextRender);
setDetails(ev.target.value);
} else {
//todo
}
}
}
const displayItem = useMemo(() => show[currentIndex], [show, currentIndex]);
return (
<div className="container" id="container">
<div className="navigation">
<ol>
<li>{this should display their name}</li>
<li>{this should display their username}</li>
<li>{this should display their email}</li>
</ol>
</div>
<form id="sign-form" className="sign-form">
<ol className="questions">
{
<li onKeyPress={(KeyboardEvent) => onKeyPressed(KeyboardEvent, displayItem.id)} key={displayItem.id} >
<span><label htmlFor={displayItem.label}>{displayItem.text}</label></span>
<input id={displayItem.id} name={displayItem.label} type="text" placeholder={displayItem.placeholder} autoFocus/>
</li>
};
</ol>
</form>
</div>
)
Right, I think I know what you mean now. I've run the code in CodeSandbox and it makes sense. You want to do like a stepper for your registration where you ask a single question at a time.
Storing values in an object would still be a preferable way of doing this. But you need to get the label for the value and append it to the existing object. You can build your object with data when you go through your stepper.
Here is a working solution. Hopefully that's what you were looking for: https://codesandbox.io/s/unruffled-pasteur-76xux4?file=/src/App.js
I modified the onKeyPressed to grab the label from VALUES array based on the index we are currently on. Then that label is used as a key inside of the object where the value is the value from the event handler
const onKeyPressed = (ev, id) => {
if (ev.charCode === 13) {
ev.preventDefault();
const label = show[currentIndex].label; // grab the label based on the current index
const nextRender = currentIndex + 1;
if (nextRender < show.length) {
setCurrentIndex(nextRender);
setDetails({ ...details, [label]: ev.target.value }); // add the value to the details object where key is the label
} else {
//todo
}
}
};
I let you the code that it works like I think that you want
import { useState, useEffect, useRef, useMemo } from 'react';
const VALUES = [
{ id: 1, label: "name", text: "Hi, What is your Name?", placeholder: "Enter your full name" },
{ id: 2, label: "uname", text: "What shall we call you?", placeholder: "Enter a username" },
{ id: 3, label: "email", text: "Enter you email", placeholder: "Email" },
{ id: 4, label: "password", text: "Choose a password", placeholder: "make sure you dont forget" },
{ id: 5, label: "signup", text: "sign up", placeholder: ""},
];
export default function SignUp() {
const [show, setShow] = useState(VALUES)
const [currentIndex, setCurrentIndex] = useState(0);
const [details, setDetails] = useState({});
useEffect(() => {
}, [show]);
const onKeyPressed = ( ev, id ) => {
if (ev.charCode === 13) {
ev.preventDefault();
const nextRender = currentIndex + 1;
if (nextRender < show.length) {
setCurrentIndex(nextRender);
const label = VALUES[currentIndex].label;
details[label] = ev.target.value;
setDetails(details);
} else {
//todo
}
}
}
const displayItem = useMemo(() => show[currentIndex], [show, currentIndex]);
return (
<div className="container" id="container">
<div className="navigation">
<ol>
{Object.keys(details).map((key) => (
<li><a href="#" dataref={key}>{key}: {details[key]}</a></li>
))}
</ol>
</div>
<form id="sign-form" className="sign-form">
<ol className="questions">
<li onKeyPress={( KeyboardEvent ) => onKeyPressed(KeyboardEvent, displayItem.id)} key={displayItem.id}>
<span><label htmlFor={displayItem.label}>{displayItem.text}</label></span>
<input id={displayItem.id} name={displayItem.label} type="text" placeholder={displayItem.placeholder}
autoFocus/>
</li>
</ol>
</form>
</div>
)
}
i have a file contain objects data.js.
components: [
{
id: "1",
nameC: "name",
type: "TextInput",
options: { placeholder: "saisir nom", required: true },
},
{
id: "2",
nameC: "Phone",
type: "Phone",
options: { placeholder: "saisir number", required: false },
},
{
id: "3",
nameC: "name",
type: "TextInput",
options: { placeholder: "saisir nom", required: true },
},
i got those objects to create inputs Dynamically in this file TemplateScreen.js .
<>
{getData.length === 0 ? (
<Empty />
) : (
getData.map((item, index) => {
switch (item.type) {
case "TextInput":
return (
<>
<InputText
ModuleName={item.nameC}
placeholder={item.options.placeholder}
required={item.options.required}
/>
</>
);
case "Phone":
return (
<>
<Phone
ModuleName={item.nameC}
placeholder={item.options.placeholder}
required={item.options.required}
/>
</>
);
default:
return <Text>Nothing hear</Text>;
}
})
)}
</>
i render inputs successfully but i'can't handle those inputs :( .i'm tried many methods but anyone worked for me.i am tried many tricks from reactjs tuto but nothing worked for me .i'm blocked 4 days in this problem,please anyone can help me :(
this file contain TextInput component, i am called it in templateScreen.js
Phone component is the same as TextInput component with a bit of difference
export const InputText = (props) => {
const [state, setState] = React.useState("");
return (
<View style={styles.container} key={props.keys}>
<View style={styles.Namecontainer}>
<Text style={styles.moduleName}>{props.ModuleName}</Text>
{props.required ? <Text style={styles.required}>*</Text> : <></>}
</View>
<TextInput
{...props}
value={state}
onChangeText={(text) => setState(text)}
placeholder={props.placeholder}
style={styles.inputtext}
/>
</View>
);
};```
Instead of placing state inside the Input text and Phone text component, why not just use useRef hook inside the template screen js? We should generate refs depending on the length of the data, thus
const inputRefs = getData.reduce((acc,curr)=>{
const ref = useRef(“”);
acc[curr.nameC] = ref;
return acc:
}, {});
this will generate refs for each of your inputs. Now in our map method, we just place our input refs to each ex:
<InputText
inputRefs[item.nameC]
ModuleName={item.nameC}
placeholder={item.options.placeholder}
required={item.options.required}
/>
In order to get their values, map the inputRefs and try console.log(inputRef.current) to see.
I have created the replica in reactjs because react-native is not set up on my PC but the logic remains the same, as I correctly understand what you wanna do is render dynamic input form according to the data this is provided to you and store the value in the state
import React, { useEffect, useState } from "react";
const components = [
{
id: "1",
nameC: "name",
type: "TextInput",
options: { placeholder: "saisir nom", required: true },
},
{
id: "2",
nameC: "phone",
type: "Phone",
options: { placeholder: "saisir number", required: false },
},
{
id: "3",
nameC: "city",
type: "TextInput",
options: { placeholder: "saisir nom", required: true },
},
];
const DynamicInput = () => {
const [field, setField] = useState();
const handleChange = (event) => {
setField({ ...field, [event.target.name]: event.target.value });
};
useEffect(() => {
let obj = {};
components.forEach((item, index) => {
obj = { ...obj, [item.nameC]: "" };
});
setField(obj);
}, []);
console.log(field)
return (
<div>
{!field ? (
<div>Loading...</div>
) : (
components.map((item) => {
const value = field[item.nameC];
switch (item.type) {
case "TextInput":
return (
<InputText
key={item.nameC}
value={value}
onChangeHandler={handleChange}
placeholder={item.options.placeholder}
isRequired={item.options.required}
name={item.nameC}
/>
);
case "Phone":
return <div key={item.nameC}>This will be same as input</div>;
default:
return <div key={item.nameC}>Nothing hear</div>;
}
})
)}
</div>
);
};
export default DynamicInput;
export const InputText = ({
value,
onChangeHandler,
placeholder,
isRequired,
name,
}) => {
return (
<input
value={value}
name={name}
onChange={onChangeHandler}
placeholder={placeholder}
required={isRequired}
/>
);
};
The form data is set by an the data object. Need help figuring out how to update /handleChange the text inputs
I've tried unique name, those probably won't work because it wont match the "key" in the object.
Any help / input is appreciated!
Data:
export default
{
name: "Restaurants Name",
menu: [
{
category: "Appetizers",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Food 2",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Food 1",
desc: "",
price: "300"
}
]
},
{
category: "Entree",
items:
[ {
imgurl: "https://source.unsplash.com/400x200/?863127",
title: "Entree 1",
desc: "",
price: "500"
},
{
imgurl: "",
title: "Entree 1",
desc: "",
price: "300"
}
]
},
]
}
Code:
import React, { useEffect, useState } from "react";
import "./../App.css"
import MenuData from "../data"
function Edit() {
const [formState, setFormState] = useState(MenuData);
useEffect(() => {
console.log(formState)
}, []);
const handleNameChange = (event) => {
const name = event.target.name;
// console.log(name)
setFormState(prevState => ({
formState: { // object that we want to update
...prevState.formState, // keep all other key-value pairs
[name]: event.target.value, // update the value of specific key
menu: {
...prevState.menu,
items: {
...prevState.menu.items
}
}
}
}))
// setFormState({
// ...formState,
// [name]: event.target.value,
// })
};
const handleChange = (categoryIndex, event) => {
// const values = [...formState]
// values[categoryIndex][event.target.name] = event.target.value;
// setFormState(values);
const name = event.target.name;
// setFormState(prevState => ({
// formState: {
// ...prevState.formState,
// menu: {
// ...prevState.menu,
// items{
// ...prevState.items
// }
// }
// }
// }));
};
return (
<div className="App">
<div>
<input name="nameField" id="nameField" maxLength="300" value={formState.name} onChange={handleNameChange} /> <br />
{formState.menu && formState.menu.map((menuitem, categoryIndex) => {
return (
<div key={categoryIndex}>
<div class="line"></div>
<h2>{menuitem.category}</h2>
<input name={"category-" + categoryIndex} id="CategoryField" maxLength="40" categoryIndex={categoryIndex} onChange={event => handleChange(categoryIndex, event)} value={menuitem.category} />
{
menuitem.items.map((item, index) => {
return(
<div key={index}>
<input name={"title-" + index + categoryIndex} id="titleField" maxLength="40" categoryIndex={categoryIndex} onChange={handleChange} value={item.title} /> <br />
<input name="desc" id="descField" maxLength="200" categoryIndex={categoryIndex} onChange={handleChange} value={item.desc} />
<br /><br />
</div>
)
})
}
</div>
)
}
)
}
</div>
</div>
);
}
export default Edit;
UPDATED
Not able to figure out the onChange function to updated nested items
how to have dropdowns selected value in state.here is my code iam getting value for name field but dropdown not working, can anyone find out what i am missing?
MyComponent.js
import React,{Component} from 'react';
class MyComponent extends Component{
state={
data:{
name:'',
subject:''
}
}
onChange = e =>
this.setState({
data: { ...this.state.data, [e.target.name]: e.target.value }
},()=>{
console.log(this.state.data);
}
)
render(){
const {data}=this.state;
const subjects= [
{text: '1',value: 'kannada'},
{text: '2', value: 'english'},
{text: '3',value: 'hindhi'}
]
return(
<div>
<Input
name="name"
onChange={this.onChange}
placeholder='Your name ...'
/>
<Dropdown
placeholder='Select Subject'
name="subject"
onChange={this.onChange}
selection
options={subjects}
/>
</div>
)
}
}
export default MyComponent;
how to have selected dropdown value in state?, iam getting changed value for name field but for dropdown not getting.
handleChange = (e, { value }) => this.setState({ value })
Add value prop to Dropdown
render(
const { value } = this.state;
return(
<Dropdown
placeholder='Select Subject'
name="subject"
onChange={this.handleChange}
selection
options={subjects}
value={value}
/>)
)
If anyone is using react hooks and semantic ui react, this is how I got it to work, without having to create a separate change handler function for it.
const options = [
{ key: "1", text: "Speaker", value: "SPEAKER" },
{ key: "2", text: "Event Planner", value: "EVENT_PLANNER" }
];
const [values, setValues] = useState({
firstName: "",
userType: ""
});
const onChange = (event, result) => {
const { name, value } = result || event.target;
setValues({ ...values, [name]: value });
};
<Form.Dropdown
placeholder="I am a.."
name="userType"
label="User Type"
selection
onChange={onChange}
options={options}
value={values.userType}
/>
What kept throwing me off was the 'result' that the onChange function takes as an argument. Since the options are stored as objects in an array, the correct way to access their values is with the 'result' and not 'event.target.'
One more way to use DropDown in React Semantic. it worked perfectly for me.
const options = [
{ key: 'ex1', text: 'Example 1', value: 'Example 1' },
{ key: 'ex2', text: 'Example 2', value: 'Example 2' },
]
Method to set value
handleSelectChange=(e,{value})=>this.setState({stateValue:value})
In Form
<Form.Select fluid label='List Example' options={options}
placeholder='List Example'
value={value}
onChange={this.handleSelectChange} />
You can use the Form.Field also.
For more information.
constructor(props) {
super(props);
this.state={
subject : ""
}
}
handleChange = (e, result) => {
const { name, value } = result;
this.setState({
[name]: value
});
};
render() {
const options = [
{ key: 'ex1', text: 'Example 1', value: 'Example 1' },
{ key: 'ex2', text: 'Example 2', value: 'Example 2' },
];
return(
<div>
<Form>
<Form.Field
placeholder="Subject"
name="subject"
label="Subject"
control={Dropdown}
fluid
selection
onChange={this.handleChange}
options={options}
value={this.state.subject}
/>
</Form>
</div>
);
}