I have an input form for data in a table that generates inputs in accordance to how many columns there are. I am struggling to separate the inputs however so that when I change a value in one all of the inputs change. Is there any way that I can differentiate between them so that I can input different values in each input. I am using React with flux.
Here is the code I am currently working with:
import React from 'react';
import AppStore from '../../stores/AppStore';
export default class RowForm extends React.Component {
state = {input: ''};
onChange = (e) => {
this.setState({input: e.target.value});
console.log(input);
};
editStop = () => {
this.props.editStop();
};
handleSubmit = (e) => {
e.preventDefault();
let access_token = AppStore.getToken();
let id = AppStore.getTable().id;
this.props.handleSubmit(access_token, id);
};
render() {
let {input} = this.state;
let dataEntries = AppStore.getTable().columns; //this gets the amount of columns so I can map through them and generate the correct amount of inputs.
return (
<tr>
{dataEntries.map((element, index) => {
return (
<td key={index}><input type="text" className="form-control" id={element.id} placeholder="enter data" value={this.state.input} onChange={this.onChange} /></td>
);
})}
<td>
<button className="btn btn-default" onClick={this.editStop}><i className="fa fa-ban"></i>Cancel</button>
<button className="btn btn-success" onClick={this.handleSubmit}><i className="fa fa-check"></i>Save</button>
</td>
</tr>
);
}
}
Any help would be much appreciated, especially examples!
Thanks for your time
You can create an anonymous function in the onChange
handler:
<input key={index} onChange={event => this.onChange(event, index)}/>
However, the larger problem is that you are not mapping your AppStore.getTable().columns
to state anywhere so you won't be able to modify the component state at all. Also, you are using ES6 classes improperly with React.
class RowForm extends React.Component {
constructor (props) {
super(props);
this.state = {
inputs: {0: null, 1: null, 2: null}
};
}
onChange (event, index) {
this.setState({inputs[index]: event.target.value});
}
}
If you need to map AppStore.getTable().columns
you should pass that data down as props. Mapping props to state is an anti-pattern.
class App extends React.Component {
constructor () { // initialize stuff... }
componenDidMount () {
this.setState({columns: AppStore.getTable().columns});
}
onChange (event, index) {
this.setState({columns[index]: event.target.value});
}
render () {
<RowForm columns={this.state.columns} handleChange={this.onChange}/>
}
}
class RowForm extends React.Component {
constructor (props) {
super(props);
}
render () {
<div>
{this.props.columns.map(index => {
return <input onChange={event => this.props.handleChange(event, index)}/>
})}
</div>
}
}
However, this will not update the AppStore
when onChange
is called. For that you need to keep track of global state somehow. I suggest checking out Redux.
Updated answer to try and fix the code with the current conditions:
class RowForm extends React.Component {
// Set `dataEntries` to an empty array. This will prevent errors
// from appearing in between `render` and `componentDidMount`.
// e.g.: `this.state.dataEntries.map` is undefined or not an Array.
// You may want to make this a little bit more fail safe though.
state = {dataEntries: []};
onChange = (event, element) => {
// This assumes `element` is a string based on the column name.
this.setState({dataEntries[element]: event.target.value});
}
componentDidMount () {
// Set state with columns from the table.
// Whenever this component mounts it will reset to the state
// from `AppStore` unless you set up event listeners like in
// Flux or migrate to Redux
// This also assumes that `getTable().columns` returns an array
// of column names. I don't know what your data structure looks
// like so it's hard for me to help here. You need to turn the
// array into an object to store values in the keys.
let dataEntries = AppStore.getTable().columns.reduce((obj, name) => {
obj[name] = null;
return obj;
}, {});
this.setState({dataEntries});
}
render () {
let {dataEntries} = this.state;
// It is not really recommended to use indexes here because
// of the way React diffing works. If `element` is a string
// you can use that as the key/index instead. Also, it needs
// to be unique within the array.
// Turn dataEntries back into an array so map will work properly.
return (
<tr>
{Object.keys(dataEntries).map((element) => {
return (
<td key={element}><input type="text" className="form-control" id={element} placeholder="enter data" value={dataEntries[element]} onChange={event => this.onChange(event, element)} /></td>
);
})}
<td>
<button className="btn btn-default" onClick={this.editStop}><i className="fa fa-ban"></i>Cancel</button>
<button className="btn btn-success" onClick={this.handleSubmit}><i className="fa fa-check"></i>Save</button>
</td>
</tr>
);
}
}