I am new in React and I am confused, What I want is i have a drop down list with options as 1,2,3,4.... upto n. Suppose if I select on dropdown number 5 then dynamically 5 input fields should get generated. Also for each input field which is created. I should be manually able to remove them with a remove button.
I have created adding of input options but it is manual like we click on add button new option is added and when we click remove that particular option with index gets deleted. You can refer this link for code
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [{firstName: "", lastName: ""}]
};
this.handleSubmit = this.handleSubmit.bind(this);
}
addClick(){
this.setState(prevState => ({
users: [...prevState.users, { firstName: "", lastName: "" }]
}))
}
createUI(){
return this.state.users.map((el, i) => (
<div key={i}>
<input placeholder="First Name" name="firstName" value={el.firstName ||''} onChange={this.handleChange.bind(this, i)} />
<input placeholder="Last Name" name="lastName" value={el.lastName ||''} onChange={this.handleChange.bind(this, i)} />
<input type='button' value='remove' onClick={this.removeClick.bind(this, i)}/>
</div>
))
}
handleChange(i, e) {
const { name, value } = e.target;
let users = [...this.state.users];
users[i] = {...users[i], [name]: value};
this.setState({ users });
}
removeClick(i){
let users = [...this.state.users];
users.splice(i, 1);
this.setState({ users });
}
handleSubmit(event) {
alert('A name was submitted: ' + JSON.stringify(this.state.users));
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
{this.createUI()}
<input type='button' value='add more' onClick={this.addClick.bind(this)}/>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(<App />, document.getElementById('container'));
https://jsfiddle.net/mayankshukla5031/qL83cf2v/1/
But now I want generate it with dropdown, selecting the size of input options let say 5 so dynamically 5 options fields are created.
Can anybody guide me on it please.
Somthing like this should help :
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [1],
options: 1,
};
this.handleSubmit = this.handleSubmit.bind(this);
}
addClick(){
const newUsers = Array.from(Array(Number(this.state.options)), (_, i) => i);
this.setState((prevState) => ({
users: [...prevState.users, ...newUsers],
}));
};
createUI() {
return this.state.users.map((el, i) => (
<div key={i}>
<input
placeholder="First Name"
name="firstName"
value={el.firstName || ""}
onChange={this.handleChange.bind(this, i)}
/>
<input
placeholder="Last Name"
name="lastName"
value={el.lastName || ""}
onChange={this.handleChange.bind(this, i)}
/>
<input
type="button"
value="remove"
onClick={this.removeClick.bind(this, i)}
/>
</div>
));
}
handleChange(i, e) {
const { name, value } = e.target;
let users = [...this.state.users];
users[i] = { ...users[i], [name]: value };
this.setState({ users });
}
removeClick(i) {
let users = [...this.state.users];
users.splice(i, 1);
this.setState({ users });
}
handleSubmit(event) {
alert("A name was submitted: " + JSON.stringify(this.state.users));
event.preventDefault();
}
handleInput = (event) => {
event.preventDefault();
this.setState({ options: event.target.value });
};
render() {
return (
<form onSubmit={this.handleSubmit}>
{this.createUI()}
<select defaultValue={this.state.options} onChange={this.handleInput}>
{Array.from(Array(100), (_, i) => i + 1).map((opt) => (
<option>{opt}</option>
))}
</select>
<input
type="button"
value="add more"
onClick={this.addClick.bind(this)}
/>
<input type="submit" value="Submit" />
</form>
);
}
}
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I hope this one helps. Link Output keys with ur input fields.
function App({data}){
const [selectedField, setSelectedField] = useState('');
const [output, setOutput] = useState({}); // u can add a default values of the keys in inital state
useEffect(()=>{
if(selectedField !== ''){
const selectedData = data.filter(el=> name === el.name);
if(selectedData){
setOutput(selectedData);
}
}
},[selectedField, data]);
const onDataFieldRemove = (key) => {
setOutput(prevState => {
delete prevState[key]; // if u dont want unwanted data in ur database or use
// prevState[key] = null; if u want to maintain ur default keys;
return prevState;
});
}
return (<div>... add ur input fields and remove button</div>)
}
Related
I'm not very proficient with React. I'm working with a dynamic form where users should be able to dynamically add and remove input fields. I'm unable to save inputs into variables dynamically however.
Code:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
values: [],
type:[],
name: '',
frequency: '',
};
}
handleMetric(i, event) {
let values = [...this.state.values];
values[i] = event.target.value;
this.setState({ values });
console.log("Metrics: ")
console.log(values)
}
handleThreshold(i, event) {
let values = [...this.state.values];
values[i] = event.target.value;
this.setState({ values });
console.log("Threshold: ")
console.log(values)
}
addClick(){
this.setState(prevState => ({ values: [...prevState.values, '']}))
}
removeClick(i){
let values = [...this.state.values];
values.splice(i,1);
this.setState({ values });
}
createUI(){
return this.state.values.map((el, i) =>
<div key={i}>
<input type="text" value={el||''} onChange={this.handleChange.bind(this, i)} />
<input type='button' value='remove' onClick={this.removeClick.bind(this, i)}/>
</div>
)
}
render() {
return (
<div>
<div className="card">
<form onSubmit={this.handleSubmit}>
<Form.Item name="Type of metrics" label="Metric" htmlFor="type">
<Select
value={Select}
onChange={this.handleMetric}
options={metrics}
/>
</Form.Item>
<Form.Item name="amount" label="Threshold Score" htmlFor="threshold">
<Slider marks={marks} name="threshold" onChange={this.handleThreshold} />
</Form.Item>
</Form>
{this.createUI()}
<input type='button' value='add more' onClick={this.addClick.bind(this)}/>
<input type="submit" value="Submit" />
</form>
</div>
</div>
);
}
}
export default App
Both handleThreshold and handleMetric functions are not working, and giving the following errors:
Would really appreciate some help in getting the variables stored in the arrays dynamically.
Turn the class methods into class property initializers with arrow functions so this is always the component instance:
handleMetric = (i, event) => {
let values = [...this.state.values];
values[i] = event.target.value;
this.setState({ values });
console.log("Metrics: ")
console.log(values)
};
handleThreshold = (i, event) => {
let values = [...this.state.values];
values[i] = event.target.value;
this.setState({ values });
console.log("Threshold: ")
console.log(values)
};
You solved this for another callback by doing this.addClick.bind(this) inside the render method. Both approaches work. Doing the arrow function declaration means only 1 function is created in the life of the component whereas .bind in render means 1 function is created per call to render. This will likely have no recognizable performance change for your app but is something to consider.
I'm having a syntax doubt on how to update React state using hooks in 2 situations.
1) I have a state called company and a form that fills it up. In contact section, there are two inputs referring to the company employee (name and telephone number). But if the company has more than one employee to be contacted, there is an "Add More Contact" button, which must duplicate the same inputs (of course, aiming to a second contact). How can I do that? I mean, to generate another index in the array "contacts" inside the state, increment the totalOfContacts inside the object that has that array and create the input tags so user can type the second contact's data?
2) When I type any inputs, the code triggers the handleChange function. The "name" and "city" already update the state because they are simple states. But how can I update the contact name and his telephone number, since they are part of an index of an array inside the state?
The code below is already working and my 2 questions are exactly the two commented lines (lines 20 and 29).
The "Save" button simply console.log the results so we can monitor them.
Thanks for now.
import React, { useState, useEffect } from "react";
export default () => {
const [company, setCompany] = useState({
name: "", city: "",
contact: {
totalOfContact: 1,
contacts: [
{id: 0, contactName: "", telephoneNumber: ""}
]
}
})
useEffect(() => {
console.log("teste");
})
const handleChange = item => e => {
if (item === "contactName" || "telephone") {
// How can I set company.contact.contacts[<current_index>].contactName/telephoneNumber with the data typed?
} else {
setCompany({ ...company, [item]: e.target.value })
}
}
const handleClick = (e) => {
e.preventDefault();
if (e.target.value === "add") {
// How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
} else {
console.log(`The data of the company is: ${company}`);
}
}
return (
<div>
<form>
<h3>General Section</h3>
Name: <input type="text" onChange = {handleChange("name")} value = {company.name} />
<br />
City: <input type="text" onChange = {handleChange("city")} value = {company.city} />
<br />
<hr />
<h3>Contacts Section:</h3>
Name: <input type="text" onChange = {handleChange("contactName")} value = {company.contact.contacts[0].name} />
Telephone Numer: <input type="text" onChange = {handleChange("telephone")} value = {company.contact.contacts[0].telephoneNumber} />
<br />
<br />
<button value = "add" onClick = {(e) => handleClick(e)} >Add More Contact</button>
<br />
<br />
<hr />
<button value = "save" onClick = {(e) => handleClick(e)} >Save</button>
</form>
</div>
)
}
To update the state value, you can use functional setState,
const handleChange = item => e => {
//Take the value in a variable for future use
const value = e.target.value;
if (item === "contactName" || "telephone") {
setCompany(prevState => ({
...prevState,
contact: {...prevState.contact, contacts: prevState.contact.contacts.map(c => ({...c, [item]: value}))}
}))
} else {
setCompany({ ...company, [item]: e.target.value })
}
}
To add new set of input on the click of button you can do this,
const handleClick = (e) => {
e.preventDefault();
//This is new set of input to be added
const newSetOfInput = {id: company.contact.contacts.length, contactName: "", telephoneNumber: ""}
if (e.target.value === "add") {
// How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
setCompany(prevState => ({
...prevState,
contact: {...prevState.contact, contacts: prevState.contact.contacts.concat(newSetOfInput), totalOfContact: prevState.contact.contacts.length + 1}
}))
} else {
console.log(`The data of the company is: ${company}`);
}
}
Finally you need to iterate over your contacts array like,
{company.contact.contacts && company.contact.contacts.length > 0 && company.contact.contacts.map(contact => (
<div key={contact.id}>
Name: <input type="text" onChange = {handleChange("contactName")} value = {contact.contactName} />
<br/>
Telephone Numer: <input type="text" onChange = {handleChange("telephoneNumber")} value = {contact.telephoneNumber} />
</div>
))}
Demo
Note: You should use block elements like div instead of breaking the line using <br/>
To answer your question let us scope down this problem to a much simpler problem, which is how to handle array of contacts.
You just need know the following things:
Map function
How to update array without mutating the original array
I'll use TypeScript so you can understand better.
const [state, setState] = React.useState<{
contacts: {name: string}[]
}>({contacts: []})
return (
<div>
{state.contacts.map((contact, index) => {
return (
<div>
Name:
<input value={contact.name} onChange={event => {
setState({
...state,
contacts: state.contacts.map((contact$, index$) =>
index === index$
? {...contact$, name: event.target.value}
: {...contact$}
)
})
}}/>
</div>
)
}}
</div>
)
Also, this kind of problem is fairly common in React, so understand and memorize this pattern will help you a lot.
You can do something like this.
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [company, setCompany] = useState({
name: "",
city: "",
contact: {
totalOfContact: 1,
contacts: [{id: 0, contactName: "", telephoneNumber: ""}]
}
});
console.log(company);
useEffect(() => {
console.log("teste");
}, []);
const handleChange = (item, e,index) => {
if (item === "contactName" || item === "telephoneNumber") {
const contactsNew = [...company.contact.contacts];
contactsNew[index] = { ...contactsNew[index], [item]: e.target.value };
setCompany({
...company,
contact: { ...company.contact, contacts: contactsNew }
});
// How can I set company.contact.contacts[<current_index>].contactName/telephoneNumber with the data typed?
} else {
setCompany({ ...company, [item]: e.target.value });
}
};
const handleClick = e => {
e.preventDefault();
if (e.target.value === "add") {
const contactNew = {...company.contact};
contactNew.totalOfContact = contactNew.totalOfContact + 1;
contactNew.contacts.push({id:contactNew.totalOfContact -1, contactName: "", telephoneNumber: ""});
setCompany({...company, contact: {...contactNew}});
// How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
} else {
alert("Push company to somewhere to persist");
console.log(`The data of the company is: ${company}`);
}
};
return (
<div>
<form>
<h3>General Section</h3>
Name:{" "}
<input
type="text"
onChange={(e) => handleChange("name", e)}
value={company.name}
/>
<br />
City:{" "}
<input
type="text"
onChange={(e) => handleChange("city", e)}
value={company.city}
/>
<br />
<hr />
<h3>Contacts Section:</h3>
{company.contact.contacts.map((eachContact, index) => {
return <React.Fragment>
Name:{" "}
<input
type="text"
onChange={(e) => handleChange("contactName",e, index)}
value={eachContact.name}
/>
Telephone Numer:{" "}
<input
type="text"
onChange={(e) => handleChange("telephoneNumber",e, index)}
value={eachContact.telephoneNumber}
/>
<br />
</React.Fragment>
})}
<br />
<button value="add" onClick={e => handleClick(e)}>
Add More Contact
</button>
<br />
<br />
<hr />
<button value="save" onClick={e => handleClick(e)}>
Save
</button>
</form>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Your state structure looks like an ideal candidate for useReducer hook. I would suggest you try that instead of useState. Your code should look muck readable that way, I suppose. https://reactjs.org/docs/hooks-reference.html#usereducer
So, I have a form and I want the user to display the values user fills in the fields as a JSON object at the end when the user clicks the submit button.
In Form.js,
state={
group:[
type-A{col1: "",
col2:""
}
]
}
handleSubmit(event) {
event.preventDefault();
<Credentials value={JSON.stringify(this.state)}/>
}
change = e =>{
this.setState({[e.target.name]: e.target.value})
};
render(){
return(
<div class="classform">
<form >
<label>
Column1:
<br/>
<input type="text"
name="group1"
placeholder="Column1"
value={this.state.column1}
onChange={e=> this.change(e)}
//other fields
//input form fields
<button onClick={this.handleSubmit}>Submit</button>
In Credentials.js,
return (
<p>{value}</p>
)
}
export default Credentials
The above code gives me an error, in handleSubmit() in second line (<Credentials value={JSON.stringify(this.state)}/>)
When the user clicks Submit button, I want to get a JSON object for the data entered in the input fields in the form and update it if the user updates any information in the fields.
Move the component to render method. and use conditional rendering.
state = {credentials: false}
handleSubmit = event => {
event.preventDefault();
this.setState({
credentials: true // display Credentials component
});
};
render() {
return (
<div>
<button onClick={this.handleSubmit}>Submit</button>
{this.state.credentials && (
<Credentials value={JSON.stringify(this.state)} />
)}
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
const Credentials = ({ value }) => {
return <p>{value}</p>;
};
class App extends React.Component {
state = { credentials: false };
handleSubmit = event => {
event.preventDefault();
this.setState({
credentials: true // display Credentials component
});
};
change = e => {
const name = e.target.name;
const nameObj = {};
nameObj[name] = e.target.value;
this.setState({ ...nameObj });
};
render() {
return (
<div>
<input
type="text"
name="col1"
value={this.state['col1']}
onChange={e => this.change(e)}
/>
<button onClick={this.handleSubmit}>Submit</button>
{this.state.credentials && (
<Credentials value={JSON.stringify(this.state)} />
)}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
</script>
I want create a function using with i can reset value in form inputs without submit. I tried create that function in App Component (resetFormFields) and pass it on props to Form Component. It's preety simply when I want to do this onSubmit (e.target.reset()) but I got stuck when I have to do it without submit, on a different element than the form. Can I do that without adding these values to state?
App:
class App extends Component {
state = {
people: [],
formMessages: [],
person: null
};
handleFormSubmit = e => {
e.preventDefault();
const form = e.target;
const name = form.elements["name"].value;
const username = form.elements["username"].value;
this.addPerson(name, email);
form.reset();
};
resetFormFields = () => {
return;
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit}
reset={this.resetFormFields} />
</div>
);
}
Form:
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );
onHandleFormSubmit = (e) =>{
e.preventDefault();
e.target.reset();
}
You need to make your inputs controlled by passing the value you store in your state then you just have to reset the state values and your component value resets.
check this sample below
handleInputChange = (e) => {
let { name, value } = e.target;
this.setState({
...this.state,
inputs: {
[name]: value
}
});
}
your component will now look like
<input name='fullName' value={this.state.inputs.fullName} onChange={this.handleInputChange} />
Your reset function will just clear the state and your input field will be empty since it's controlled via state
resetInputFields = () => {
this.setState({ inputs: {} })
}
you should give set your input values based on component state, then just update the component state
class App extends Component {
state = {
people: [],
formMessages: [],
person: null,
name: "",
email: "",
};
updateState = (newState) => {
this.setState(newState);
}
handleFormSubmit = e => {
e.preventDefault();
this.addPerson(this.state.name, this.state.email);
form.reset();
};
resetFormFields = () => {
this.setState({name:"", email: ""});
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit} updateState={this.updateState}
reset={this.resetFormFields} email={this.state.email} name={this.state.name} />
</div>
);
}
and then
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
value={this.props.name}
onChange={(e) => this.props.updateState({name: e.target.value})}
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
value={this.props.email}
onChange={(e) => this.props.updateState({email: e.target.value})}
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );
I am working on e-commerce React/Redux project, I want to make functionality by which user can display the products according to price slider, I have made two input fields in which user can type min and max price value,This functionality is working on button click, but I want on change event, As the user type second value it filters product onChange,
can anyone help me to sort this issue, Thanks in advance, My code and screenshot is attached below
class PriceInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: props.values,
};
this.onValueChangeComplete = this.onValueChangeComplete.bind(this);
}
onValueChangeComplete() {
const { onValueChange } = this.props;
onValueChange(this.state.value);
}
render() {
const { currencyCode, limits } = this.props;
const { value } = this.state;
const notChanged = _.isEqual(value, limits);
return (
<div className={styles.wrapper}>
<div className={styles.inputWrapper}>
{I18n.getComponent(
(formattedValue) =>
<input
type="text"
name="min"
className={styles.textInput}
placeholder={formattedValue}
/>,
'filter.price-range-min'
)}
<span className={styles.between}>{I18n.getText('filter.price-aed', {}, 'To')}</span>
{I18n.getComponent(
(formattedValue) =>
<input
type="text"
name="max"
className={styles.textInput}
placeholder={formattedValue}
/>,
'filter.price-range-min'
)}
</div>
</div>
);
}
}
Component in which I have to used the price functionality
case 'price':
childComponent = (
<PriceInput values={facet.data}
limits={facet.data}
currencyCode={this.props.currency.code}
onValueChange={(data) => this.onSearchChange(facet.code, data)}/>
);
break;
It's gonna be something like:
handleMaxChange(event) {
const minValue = '' // I'm assuming you already have max's value saved somewhere
const { value } = event.target
if (minValue && value) {
this.props.onValueChange(/* send values here */)
}
}
render() {
return (
...
<input
type="text"
name="max"
className={styles.textInput}
placeholder={formattedValue}
onChange=={handleMaxChange}
/>
)
}