I'm trying to make a little D&D initiative tracker. I'm trying to modify a form to take multiple inputs and generate a dynamic table.
My current issue is that when I submit the form, a 4x4 table is created with each input displaying across each column of a row instead of one row with one input per column. I'm still a little fuzzy on props so I'm not sure if that's where my disconnect is or somewhere else.
Code is based on this.
This is DynamicTable.jsx;
import React from 'react';
export default class DynamicTable extends React.Component {
constructor(props) {
super(props);
this.state = {
initiative: "",
name: "",
armorClass: "",
hitPoints: "",
combatants: []
}
}
updateInitiative(event) {
this.setState({initiative: event.target.value});
}
updateName(event) {
this.setState({name: event.target.value});
}
updateArmorClass(event) {
this.setState({armorClass: event.target.value});
}
updateHitPoints(event) {
this.setState({hitPoints: event.target.value});
}
handleClick() {
var combatants = this.state.combatants;
combatants.push(this.state.initiative);
combatants.push(this.state.name);
combatants.push(this.state.armorClass);
combatants.push(this.state.hitPoints);
this.setState({
combatants: combatants,
initiative: "",
name: "",
armorClass: "",
hitPoints: ""
});
}
handleCombatantChanged(i, event) {
var combatants = this.state.combatants;
combatants[i] = event.target.value;
this.setState({
combatants: combatants
});
}
handleCombatantDeleted(i) {
var combatants = this.state.combatants;
combatants.splice(i, 1);
this.setState({
combatants: combatants
});
}
renderRows() {
var context = this;
return this.state.combatants.map(function(o, i) {
return (
<tr key={"combatant-" + i}>
<td>
<input
id="initiative"
type="text"
value={o}
onChange={context.handleCombatantChanged.bind(context, i)}
/>
</td>
<td>
<input
id="name"
type="text"
value={o}
onChange={context.handleCombatantChanged.bind(context, i)}
/>
</td>
<td>
<input
id="armorClass"
type="text"
value={o}
onChange={context.handleCombatantChanged.bind(context, i)}
/>
</td>
<td>
<input
id="hitPoints"
type="text"
value={o}
onChange={context.handleCombatantChanged.bind(context, i)}
/>
</td>
<td>
<button onClick={context.handleCombatantDeleted.bind(context, i)}>
Finish him!
</button>
</td>
</tr>
);
});
}
render() {
return (
<div>
<table>
<td>
<th>Initiative</th>
<input
id="initiative"
type="text"
value={this.state.initiative}
onChange={this.updateInitiative.bind(this)}
/>
</td>
<td>
<th>Name</th>
<input
id="name"
type="text"
value={this.state.name}
onChange={this.updateName.bind(this)}
/>
</td>
<td>
<th>Armor Class</th>
<input
id="armorClass"
type="text"
value={this.state.armorClass}
onChange={this.updateArmorClass.bind(this)}
/>
</td>
<td>
<th>Hit Points</th>
<input
id="hitPoints"
type="text"
value={this.state.hitPoints}
onChange={this.updateHitPoints.bind(this)}
/>
</td>
<td><th></th>
<button onClick={this.handleClick.bind(this)}>
Add Combatant
</button>
</td>
</table>
<table className="">
<thead>
<tr>
<th>Initiative</th>
<th>Name</th>
<th>Armor Class</th>
<th>Hit Points</th>
<th>Kill?</th>
</tr>
</thead>
<tbody>
{this.renderRows()}
</tbody>
</table>
</div>
);
}
}
This is App.js;
import DynamicTable from './DynamicTable';
function App() {
return (
<div className="App">
<DynamicTable />
</div>
);
}
export default App;
The difference with the link you provided is that instead of just a message, you want to display a combatant. Which is totally fine. I think you want your combatants array to be an array of objects, like this:
[
{
initiative: "13",
name: "Bel",
armorClass: "18",
hitPoints: "81",
}
]
Instead of this :
[
"13",
"Bel",
"18",
"81",
]
You get 4 rows because you append your combatants array 4 times. Just append it once but with an object which contains all information for one combatant :
combatants.push({
initiative: this.state.initiative,
name: this.state.name,
armorClass: this.state.armorClass,
hitPoints: this.state.hitPoints,
})
But an additional change is needed. Now, your array of combatants is no longer an array of strings but an array of objects you will have to change the value displayed in your inputs of the renderRows
. All your values display the same thing o
which was a string. Now, o
will be an object and you need to access values of your combatant object, for example :
value={o.initiative}