Looping Through Text Inputs in React - javascript

I'm building an app where I want the user to specify a number of text fields and then render this amount of fields dynamically. I'm having trouble setting up the state so that it is unique for each field. Here is the code segment:
var fieldsArray = [];
for(var i = 0; i <= this.props.numToShow; i ++){
fieldsArray.push(
<div>
<label>
<div className="label">{i}</div>
<input type='text' value={this.state.value} name={this.state.value} onChange={this.handleChange} />
</label>
</div>
);
}
return (
<div className = 'inputs'>
{fieldsArray}
</div>
);
Currently, when I change one of the fields, all the other fields update with that unique fields state. Here is the handleChange function that sets the state:
handleChange: function(e){
this.setState({
value: e.target.value,
});
}
Is it possible to initialize the state as an array and keep track of the inputs that way? Or is there a better way to do this?

Keeping an array of values in state would work fine. You'll just have to make sure you're passing the index of the input so you know what to update. bind helps with this:
class YourComponent extends React.Component {
constructor(props) {
super(props);
this.state = { values: [] };
}
handleChange(i, e) {
this.setState({
values: { ...this.state.values, [i]: e.target.value }
});
}
render() {
var fieldsArray = [];
for (var i = 0; i <= this.props.numToShow; i++) {
fieldsArray.push(
<div>
<label>
<div className="label">{i}</div>
<input
type='text'
value={this.state.values[i]}
name={this.state.values[i]}
onChange={this.handleChange.bind(this, i)} />
</label>
</div>
);
}
return (
<div className = 'inputs'>
{fieldsArray}
</div>
);
}
}

onChange={(e) => {
var newPresetList = [...presetList]
newPresetList.map((_item) => {
if (_item.id === item.id) {
_item.preset_name = e.target.value
return
}
})
setPresetList(newPresetList)
}}

Related

TypeError: Cannot read property 'values' of undefined in Reactjs

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.

How to get the values for dynamically generated input field in react js?

Hii I am new to React Js, After selecting count InputCount, I had generated input fields dynamically.Now I want to get the value for the inputs field and also I want to validate all the fields.
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.onChangeCount = this.onChangeCount.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {,
InputCount: "",
}
}
onChangeCount(e) {
this.setState({
InputCount: e.target.value,
})
}
render() {
let options1 = []
for (let j = 0; j <= this.state.InputCount; j += 1) { options1.push(j); }
return (
{this.state.InputCount && (
options1.map((i) => {
return (<div>
<input type="text" name="name" placeholder="Name" /><br />
<input type="text" name="name" placeholder="RollNumber" />
</div>)
})
)}
please help me to get value from the input fields and also validate the form.
Name input field unique by combining with the loop index.
And don't forget to use key when map item.
class App extends React.Component {
constructor(props) {
super(props);
this.onChangeCount = this.onChangeCount.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
InputCount: 2,
}
}
onChangeCount(e) {
this.setState({InputCount: e.target.value })
}
handleChange = (e) => {
console.log('name: ', e.target.name, ' value:', e.target.value)
// Do what you want here
}
render() {
let options1 = []
for (let j = 0; j <= this.state.InputCount; j += 1) { options1.push(j) }
return (
<div>
{options1.map((i) => {
return (
<div key={`group-input-${i}`}>
<input onChange={this.handleChange} type="text" name={`${i}-name`} placeholder="Name" /><br />
<input onChange={this.handleChange} type="text" name={`${i}-rollNumber`} placeholder="RollNumber" />
</div>
)})}
</div>
)}}
Now I want to get the value for the inputs field
To address each field separately, you need to name all of them uniquely. So your JSX for the fields needs to have a distinct name attribute for each field:
options1.map((i) => {
return (<div>
<input type="text" name="name-{ i }" placeholder="Name" /><br />
<input type="text" name="rollnumber-{ i }" placeholder="RollNumber" />
</div>)
})
Now each input element will have a name attribute unique for that element. Assuming options1 contains values 0 through 2:
name-0
rollnumber-0
name-1
rollnumber-1
name-2
rollnumber-2
You will get the values for the field by interrogating the field by its name attribute.

React adding/removing items to an array

so I am learning react js and i have stumbled upon a problem which i can't seem to solve. So i have one input that sets the number of break points, and as that number get bigger, more inputs are rendered, and those inputs are for giving a value for each 'breakpoint'. Now this is what i can't seem to figure out, if I type in for example '20' and '30' they are added to the array, no problem, however if I want to change the value of the first one(20) to a lower value, let's say 10 I can't figure out how to remove the existing 20 and replace it with a new one(10)...
Here's the codepen: https://codepen.io/anon/pen/MVZMRq
so far i have this:
class App extends React.Component {
constructor() {
super();
this.state = {
breakPointsCount: 0,
range: []
}
}
addBreakPoints(event) {
this.setState({
breakPointsCount: parseInt(event.target.value, 10),
progress: 0,
range: []
});
}
addBreakPointValue(event) {
const enteredValue = parseInt(event.target.value, 10);
const range = this.state.range.slice(0);
const breakpointsCount = this.state.breakPointsCount;
if (range.length < breakpointsCount) {
range.push(enteredValue);
}
this.setState({
range: range,
progress: 0,
});
}
render() {
const range = this.state.range;
const breaks = Array.from(Array(this.state.breakPointsCount));
return (
<div className="progress">
[{range.map((item, i) => (
<div key={item}>
<span className="break-point-value">{item}</span>
</div>
))}]
<div className="progress-options">
<label>Change count of break points (up to 10) </label>
<input type="number"
min="0"
max="10"
name="numberInput"
className="app-input"
onChange={this.addBreakPoints.bind(this)}
/>
</div>
<div className="progress-options">
<label>Change a value for each break point </label>
{breaks.map((item, i) => (
<input type="number"
key={`break-${i}`}
className="app-input"
onBlur={this.addBreakPointValue.bind(this)}
/>
))}
</div>
</div>
)
}
}
React.render(<App />, document.getElementById('app'));
You will need to keep track of which input got changed, so you would want to pass some kind of id to the input.
I recommend using a Component composition instead of binding and passing parameters to the inline handler.
You can write a small and simple Input component that all it does is getting a value and id and passing it back up onChange (or onBlur) in your case.
Then your change handler could look something similar to this:
addBreakPointValue = (value, id) => {
this.setState(({range}) => {
const nextRange = [...range];
nextRange[id] = value;
return{
range: nextRange
}
});
}
I wrote a simple example with your code, note that i changed some stuff, like using arrow functions as handlers (class members) so we can take advantage of their lexical context with this instead of binding the functions to the class.
class Input extends React.Component {
onBlur = ({ target }) => {
const { id, onBlur } = this.props;
onBlur(target.value, id);
}
render() {
const { value } = this.props;
return (
<input
type="number"
min="0"
max="10"
value={value}
onBlur={this.onBlur}
/>
)
}
}
class App extends React.Component {
state = {
breakPointsCount: 0,
range: []
}
addBreakPoints = ({ target }) => {
const val = parseInt(target.value, 10);
this.setState({ breakPointsCount: val });
}
addBreakPointValue = (value, id) => {
this.setState(({range}) => {
const nextRange = [...range];
nextRange[id] = value;
return{
range: nextRange
}
});
}
render() {
const { range, breakPointsCount } = this.state;
const breaks = Array.from(Array(breakPointsCount));
return (
<div className="progress">
<div className="progress-bar-wrapper">
<div className="progress-bar" style={{ width: `${this.state.progress}%` }} />
[{range.map((item, i) => (
<div key={item}>
<span className="break-point-value">{item}</span>
</div>
))}]
</div>
<div className="progress-options">
<label>Change count of break points (up to 10) </label>
<input type="number"
min="0"
max="10"
name="numberInput"
className="app-input"
onChange={this.addBreakPoints.bind(this)}
/>
</div>
<div className="progress-options">
<label>Change a value for each break point </label>
{breaks.map((item, i) => (
<Input type="number"
key={i}
id={i}
value={item}
className="app-input"
onBlur={this.addBreakPointValue}
/>
))}
</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Here is the link to fixed codepen. I have made two changes.
First in addBreakPoints method i init the range to array of the size of the input initialized with zeroes (for 5 its gonna be [0,0,0,0,0])
addBreakPoints(event) {
this.setState({
breakPointsCount: parseInt(event.target.value, 10),
progress: 0,
range: [...Array(parseInt(event.target.value, 10)).map(()=>0)]
});
}
next in render() method i bind addBreakPointValue to pass the index of the element to be updated.
{breaks.map((item, i) => (
<input type="number"
key={`break-${i}`}
className="app-input"
onBlur={this.addBreakPointValue.bind(this,i)}
/>
))}
and finally in addBreakPointValue i only update the desired element (map function return same value for all indexes except new value for the index that is passed as parameter to addBreakPointValue)
addBreakPointValue(index, event) {
const enteredValue = parseInt(event.target.value, 10);
this.setState((prevState) => ({range : prevState.range.map((r,i) => {
if(i != index) return r
return enteredValue
})}))
}
Hope it is helpful for you.
Edit: Although it now updates values, there is still issues with this code. I will propose a complete solution later.

Price filtering in reactjs

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}
/>
)
}

input filtering in reactjs

I am working on e-commerce React/Redux project, I want to make functionality by which user can display the products according to price filter, I have made two input fields in which user can type min and max price value and can display the products which lie between the price range,
the functionality is working onChange but not displaying the products between the range, it is displaying general products
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}
onChange={this.onValueChangeComplete}
/>,
'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;
This is not really a fix (I don't think) but maybe it can bring you closer to a solution. I made some edits to your code and placed comments where I made changes.
class PriceInput extends React.Component {
constructor(props) {
super(props);
this.state = {
// NOTE: I don't know how props.values looks. maybe this is wrong
min: props.values.min,
max: props.values.max
};
this.onValueChangeComplete = this.onValueChangeComplete.bind(this);
}
onValueChangeComplete(minOrMax, newValue) {
const { onValueChange } = this.props;
this.setState(
{[minOrMax]: newValue}, // update the property "min" or "max" with the new value
() => onValueChange(this.state) // when the state change is done, send both min and max to onValueChange
);
// onValueChange(this.state.value);
}
render() {
// not sure what "limits" are used for
// maybe you want to use an input with type="number" and
// use the attributes "min" and "max" ? https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number
const { currencyCode, limits } = this.props;
const { min, max } = this.state; // this is new.
const notChanged = _.isEqual(value, limits);
return (
<div className={styles.wrapper}>
<div className={styles.inputWrapper}>
{I18n.getComponent(
(formattedValue) =>
<input
value={ min } // this is new
type="text"
name="min"
className={styles.textInput}
placeholder={formattedValue}
onChange={ event => this.onValueChangeComplete('min', event.target.value) } // this was missing before
/>,
'filter.price-range-min'
)}
<span className={styles.between}>{I18n.getText('filter.price-aed', {}, 'To')}</span>
{I18n.getComponent(
(formattedValue) =>
<input
value={ max } // this is new
type="text"
name="max"
className={styles.textInput}
placeholder={formattedValue}
onChange={ event => this.onValueChangeComplete('max', event.target.value )}
/>,
'filter.price-range-min'
)}
</div>
</div>
);
}
}

Categories