I have following code where i am adding dynamic row and column. I want to delete row which on click.
But, getting difficulty figure it out.
class TableCells extends Component {
onDeleteEvent = event => {
// Delete row
};
render() {
var numberOfRow = this.props.numberOfRow; // user input
var numberOfColumn = this.props.numberOfColumn; // user input
var rows = Array.from({length: numberOfRow}).map((_, rowIdx) => (
<tr key={rowIdx}>{
Array.from({length: numberOfColumn}).map((_, colIdx) => (
<EditableCell key={colIdx}/>
))
}
<td>
<input type="button" onClick={this.onDeleteEvent} value="X" />
</td>
</tr>
))
return (<tbody>{rows}</tbody>);
}
}
Any help would be greatly appreciated.
Check this sandbox.
A little simplified, but works for you.
https://codesandbox.io/s/9l9wnonyp
Basically, move your object in the state, and when you delete the item send his ID as parameter, and just set the new state, and it will rerender.
<input
type="button"
onClick={() => this.onDeleteEvent(rowIdx)}
value="X"
/>
And onDeleteFunction:
onDeleteEvent = id => {
// Delete row
let rows = this.state.rows;
rows.splice(id, 1);
this.setState({
rows
});
};
For any other question just comment here, and I will help you :)
The right way to do this would be to:
class TableCells extends Component {
render() {
const onDeleteEvent = (id) => () => this.props.onDeleteEvent(id);
var numberOfRow = this.props.numberOfRow; // user input
var numberOfColumn = this.props.numberOfColumn; // user input
var rows = Array.from({length: numberOfRow}).map((_, rowIdx) => (
<tr key={rowIdx}>{
Array.from({length: numberOfColumn}).map((_, colIdx) => (
<EditableCell key={colIdx}/>
))
}
<td>
<input type="button" onClick={onDeleteEvent(colIdx)} value="X" />
</td>
</tr>
))
return (<tbody>{rows}</tbody>);
}
}
And wherever you use TableCellsand presumably store the number of rows you are passing as a prop to TableCells, you would have a function that reduces the number of rows passed, thus affecting the numberOfRow prop.
I'm guessing you are using TableCells something like this:
<TableCells numberOfRow={this.state.numberOfRow} numberOfColumn={this.state.numberOfColumn} />
You should change it like this:
<TableCells
numberOfRow={this.state.numberOfRow}
numberOfColumn={this.state.numberOfColumn}
onDeleteEvent={(idOfColumnToDelete) => {//Modify state here...})}
/>
You can try this one
class TableCells extends Component {
state = {
numRows = this.props.numberOfRow // I know it is considered as an antipattern :) But in this case that's ok!!!
}
onDeleteEvent = () => {
// also you should add the check if it's not zero :)
this.setState({ numRows: this.state.numRows - 1 });
};
render() {
const { numberOfColumn } = this.props; // user input
const { numRows } = this.state;
const rows = Array.from({length: numRows }).map((_, rowIdx) => (
<tr key={rowIdx}>
{
Array.from({length: numberOfColumn}).map((_, colIdx) => (
<EditableCell key={colIdx}/>
))
}
<td>
<input type="button" onClick={this.onDeleteEvent} value="X" />
</td>
</tr>
));
return (<tbody>{rows}</tbody>);
}
}
class TableCells extends Component {
constructor () {
super()
this.state = {
numberOfRow: Array.from({length: this.props.numberOfRow}),
numberOfColumn: Array.from({length: this.props.numberOfColumn})
}
}
onDeleteEvent (index) {
this.state.numberOfRow.splice(index, 1)
};
render () {
var rows = this.state.numberOfRow.map((elem, index) => (<tr>
{this.state.numberOfColumn.map((_, colIdx) => {<EditableCell key={colIdx}/>})}
<td>
<input type='button' onClick={() => this.onDeleteEvent(index)} value='X' />
</td>
</tr>))
return (
<tbody>{rows}</tbody>
)
}
}
Related
I have Built a table in which the user does some calculations, but when any row gets removed the values entered appear in the row below it and the next row's value below it, and so on.
What I want is a the user removes any row it should get removed completely with the values entered and the next row should take its place with its own values.
Image 1:Here I have entered values in the First Two rows you can see in the image.
[1]: https://i.stack.imgur.com/wWUBE.png
Image 2: I deleted the first row but as you can see the value of that is still there.
[2]: https://i.stack.imgur.com/HuOuA.png
App.js
const [NewRow2, setNewRow2] = useState(data);
const [IntensificationRatio, setIntensificationRatio] = useState(0)
const [editFormData, setEditFormData] = useState({
Injection_Speed: "",
Fill_Time: "",
Peak_Inj_Press: "",
Viscosity: "",
Shear_Rate: ""
})
const [isRowId, setIsRowId] = useState(null)
const handleEditFormChange = (event) => {
event.preventDefault();
const fieldName = event.target.getAttribute("name");
const fieldValue = event.target.value;
const newFormData = { ...editFormData };
newFormData[fieldName] = fieldValue;
setEditFormData(newFormData);
}
const handleEditFormSubmit = (event) => {
event.preventDefault();
const editedValue = {
id: isRowId,
Injection_Speed: editFormData.Injection_Speed,
Fill_Time: editFormData.Fill_Time,
Peak_Inj_Press: editFormData.Peak_Inj_Press,
Viscosity: editFormData.Fill_Time * editFormData.Peak_Inj_Press * IntensificationRatio,
Shear_Rate: 1 / editFormData.Fill_Time,
}
const newValues = [...NewRow2];
const index = NewRow2.findIndex((value) => value.id === isRowId)
newValues[index] = editedValue;
setNewRow2(newValues);
}
const deleteRow2 = (id) => {
const updatedRows = [...NewRow2].filter((rowId) => {
return rowId !== id;
});
setNewRow2(updatedRows);
};
HandlEditchange and HandleEditSubmit are the two functions that deal with data entered in the row and deleteRow2 for removing the row.
Table Row's
<tr onClick={() => setId(NewRow.id)}>
<td> {rowId} </td>
<td> <input type='text' className="form-control" name="Injection_Speed" onChange={handleEditFormChange}/> </td>
<td> <input type='text' className="form-control" name="Fill_Time" onChange={handleEditFormChange}/> </td>
<td> <input type='text' className="form-control" name="Peak_Inj_Press" onChange={handleEditFormChange}/> </td>
<td> <i className="fa fa-trash viscocity_icons" onClick={() => deleteRow2(NewRow.id)}></i> </td>
</tr>
CodeSandBox Link: Rest You Can Check Over Here.
https://codesandbox.io/s/focused-gauss-lql7r?file=/src/Edit.js
There were a few issues as I identified.
handleEditFormChange was not updating NewRow2 state. You can fix it like below. Need to pass row id along with the event.
const handleEditFormChange = (event, id) => {
event.preventDefault();
setNewRow2((prevState) => {
return prevState.map((item) =>
item.id === id
? { ...item, [event.target.name]: event.target.value }
: item
);
});
};
Need to change the onChange handlers like below.
onChange={(e) => handleEditFormChange(e, NewRow.id)}
Need to bind values from the NewRow2 to each input.
value={NewRow.Injection_Speed}
same for Fill_Time, Peak_Inj_Press
deleteRow2 should be using id correctly when filtering.
const deleteRow2 = (id) => {
const updatedRows = NewRow2.filter((item) => {
return item.id !== id;
});
setNewRow2(updatedRows);
};
Pass the id of the row to be deleted in the onClick handler.
onClick={() => deleteRow2(NewRow.id)}
Code sandbox => https://codesandbox.io/s/stoic-hopper-zi8mw?file=/src/Edit.js:2738-2775
Fairly new with React and wanting to build a simple front end application (no database) with React.
The app is a trivia game and it involves creating "users" which involves a id #, name, a "answers" field to keep track of the answers they've provided and "points" which is an integer of 0 until the player answers a question correctly.
THE PROBLEM is creating a function to add +1 to the users 'answers'. Right now when I complete the function, it saves the state as a object and therefore gives my map() function errors.
My code:
import { Button } from "bootstrap";
import React, { Component, useState } from "react";
import { Header, Table } from "semantic-ui-react";
import NewPlayer from "./NewPlayer";
import DeletePlayer from "./DeletePlayer";
// STATE
class Trivia extends Component {
constructor(props) {
super(props);
this.state = {
players: [{ name: "PLAYER", id: 0, answers: 0, points: 0 }], // Need to change this
};
this.deletePlayer = this.deletePlayer.bind(this);
this.handleChange = this.handleChange.bind(this);
this.addPlayer = this.addPlayer.bind(this);
this.answerYes = this.answerYes.bind(this);
}
// What I want it to do: onClick, add +1 to answers (eventually, add +1 to points)
deletePlayer = (e, nid) => {
e.preventDefault();
var players = this.state.players;
for (var i = 0; i < players.length; i++)
if (players[i].id && players[i].id === nid) {
players.splice(i, 1);
break;
}
this.setState({ players: players });
};
addPlayer = (e) => {
e.preventDefault();
if (!this.state.newPlayerName) {
return;
}
var currentPlayers = this.state.players;
const lastID = currentPlayers[currentPlayers.length - 1];
var variable = lastID.id + 1;
const newPlayer = {
name: this.state.newPlayerName,
id: variable,
answers: 0,
points: 0,
};
this.setState({ players: [...this.state.players, newPlayer] });
document.getElementById("name").value = "";
};
// FUNCTION THAT NEEDS HELP !!!
answerYes = (e, id) => {
// On click, answer goes up +1
e.preventDefault();
var players = this.state.players;
this.setState({ players: this.state.players[id].answers + 1 });
};
answerNo = (e, id) => {
e.preventDefault();
// Subtract point away
};
handleChange(e) {
this.setState({ newPlayerName: e.target.value });
}
render() {
var { players } = this.state;
console.log(players);
return (
<div>
<Header as="h1">Players</Header>
<Table>
<thead>
<tr>
<th>Player #</th>
<th>Name</th>
<th>Answers</th>
<th>Points</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{players.map((player) => {
return (
<tr>
<td>{player.id}</td>
<td>{player.name}</td>
<td>
{player.answers}
<button
onClick={(e) => {
this.answerYes(e, player.id);
}}
>
Correct
</button>
<button
onClick={(e) => {
this.answerNo(e, player.id);
}}
>
Incorrect
</button>{" "}
</td>
<td>{player.points}</td>
<td>
<button
onClick={(e) => {
this.deletePlayer(e, player.id);
}}
>
Delete
</button>
</td>
</tr>
);
})}
</tbody>
</Table>
<div>
<form onSubmit={this.addPlayer}>
<input
type="text"
value={this.state.name}
id="name"
placeholder="add
player name...."
onChange={this.handleChange}
/>
<input type="submit" value="add player" />
</form>
</div>
</div>
);
}
}
export default Trivia;
Right now the error I am getting is : Uncaught TypeError: players.map is not a function. I need help saving the state object. Thank you
The problem is that you are trying to map var players which does not exist before clicking button
<button
onClick={(e) => {
this.answerYes(e, player.id);
}}>
You should change the logic with your requirement flow or alternatively you can assign var player at first render in useEffect then can change in function
useEffect(() => {
var player = this.state.players
}, [] )
Say I have a table:
<div>
<tr>
<td>
<p id='1' className="foo"> Boom </p>
</td>
<td>
<p id='2' className="foo"> Bang </p>
</td>
<td>
<p id='3' className="foobar"> Pew Pew </p>
</td>
</tr>
</div>
I want the data inside it to be editable in-place. Thus I want to substitute <p> element with an <input> and then substitute it with <p> again, but with new value. I've been doing it with jQuery and now made it with what seems to me as plain JS but with React. Code looks like this:
import React, { Component } from "react";
import "./App.css";
class App extends Component {
handleClick(e) {
if (e.target.className === 'foo'){
let element = document.getElementById(e.target.id)
let element_value = element.innerText
let parent_element = element.parentNode
let new_element = document.createElement('input')
parent_element.removeChild(element)
parent_element.appendChild(new_element)
new_element.setAttribute('class', 'input')
new_element.setAttribute('id', e.target.id)
new_element.setAttribute('value', element_value)
} else if (e.target.className === 'input') {
let element = document.getElementById(e.target.id)
let element_value = element.value
let parent_element = element.parentNode
let new_element = document.createElement('p')
parent_element.removeChild(element)
parent_element.appendChild(new_element)
new_element.setAttribute('class', 'foo')
new_element.setAttribute('id', e.target.id)
new_element.innerText = element_value
}
};
componentDidMount() {
document.addEventListener('dblclick', this.handleClick)
}
componentWillUnmount() {
document.removeEventListener('dblclick', this.handleClick)
}
render() {
return (
<div>
<tr>
<td>
<p id='1' className="foo"> Boom </p>
</td>
<td>
<p id='2' className="foo"> Bang </p>
</td>
<td>
<p id='3' className="foobar"> Pew Pew </p>
</td>
</tr>
</div>
)
}
}
export default App
However this doesn't seem to me as a good practice. Could you give me hints on how to improve/change my approach? Thank you.
The best approach is probably to create a controlled component that handles all the logic for the editable cell, and store the values in the parent. I made a sandbox that you can check out here, but I'll add the code here as well.
That way the cell component provides all the view stuff needed, and the parent controls the logic and data for all the cells.
So, the editable cell handles the functionality of switching between views:
const EditableCell = ({ id, onEdit, className, value }) => {
const [isEditing, setIsEditing] = useState(false);
const onClick = useCallback(() => {
setIsEditing(true);
}, []);
const onFinishedEditing = useCallback(() => {
setIsEditing(false);
}, []);
const onKeyDown = useCallback(
(e) => {
if (e.key === "Enter") {
onFinishedEditing();
}
},
[onFinishedEditing]
);
return (
<td>
{isEditing ? (
<input
value={value}
onChange={(e) => onEdit(e.target.value, id)}
onBlur={onFinishedEditing}
onKeyDown={onKeyDown}
autoFocus
/>
) : (
<p {...{ id, className, onClick }}>{value}</p>
)}
</td>
);
};
And then the app stores the cells' data and renders an EditableCell for each one:
export default function App() {
// This stores the cells values and properties, you can
// add or remove cells here are needed
const [cellValues, setCellValues] = useState([
{ id: "1", class: "foo", value: "Boom" },
{ id: "2", class: "foo", value: "Bang" },
{ id: "3", class: "foobar", value: "Pew Pew" }
]);
const onEdit = (value, id) => {
setCellValues(
cellValues.map((cellVal) =>
cellVal.id === id ? { ...cellVal, value } : cellVal
)
);
};
return (
<div>
Click a cell to edit
<tr>
{cellValues.map((cellVal) => (
<EditableCell
id={cellVal.id}
value={cellVal.value}
className={cellVal.class}
onEdit={onEdit}
/>
))}
</tr>
</div>
);
}
This might not perfectly match with the functionality you're wanting, but should give you a starting point
I've been promise to myself that i will made good deed at least one per day.I know that you write in class way but i stick to hooks so much that... sorry man :P
it call onChange when during editing you will press enter.
import React, { Component, useEffect, useMemo, useRef, useState } from "react";
import "./styles.css";
const Td = ({ children, editable = false, onChange, className, id }) => {
const cell = useRef();
const [edit, setEdit] = useState(false);
const [value, setValue] = useState(() => {
while (typeof children !== "string") {
children = children.props.children;
}
return children;
});
const [oldValue, setOldValue] = useState(value);
useEffect(() => {
if (!cell.current) return;
const onEditMode = () => editable && setEdit(true);
const target = cell.current;
target.addEventListener("click", onEditMode);
return () => {
target.removeEventListener("click", onEditMode);
};
}, [cell, setEdit, editable]);
const paragraph = useMemo(() => (
<p id="1" className="foo">
{value}
</p>
),[value]);
const input = useMemo(() => {
const update = (value) => {
setEdit(false);
if (onChange && typeof onChange === "function") {
onChange({
id,
newValue: value,
oldValue: oldValue
});
setOldValue(value);
}
}
return (
<input
value={value}
onChange={ e => setValue(e.target.value)}
onKeyDown={ e => e.key === "Enter" && update(value)}/>
)
},[value, setEdit, onChange, id, oldValue, setOldValue]);
return (
<td ref={cell} className={className}>
{edit ? input : paragraph}
</td>
);
};
class App extends Component {
componentDidMount() {
}
componentWillUnmount() {
}
tableCellValueChange({ id, newValue, oldValue }) {
console.log(
`table cell id: ${id} value changed from ${oldValue} to ${newValue}`
);
}
render() {
return (
<div>
<table>
<thead></thead>
<tbody>
<tr>
<Td
onChange={this.tableCellValueChange}
id="special"
editable>
<p>Bang </p>
</Td>
<Td onChange={this.tableCellValueChange} editable>
<p>Bang</p>
</Td>
<Td editable={false} className="forbar">
Pew Pew
</Td>
</tr>
</tbody>
</table>
</div>
);
}
}
export default App;
here you have sandbox
live example
I render a React list and there is an Edit button in each list item. I wanted to toggle to switch from the data to the input form and back. Similar to this application in this article: https://medium.com/the-andela-way/handling-user-input-in-react-crud-1396e51a70bf. You can check out the demo at: https://codesandbox.io/s/fragrant-tree-0t13x.
This is where my React Component display the list:
import React, { Component } from "react";
import PriceBox from "../SinglePricebox/index";
// import SecurityForm from "../SecurityForm/index";
import AddPriceForm from "../AddPriceForm/index";
// import { uuid } from "uuidv4";
export default class PriceForm extends Component {
constructor(props) {
super(props);
this.state = {
priceArr: this.props.pricelist,
// newPriceArr: this.props.updatePrice,
showPricePopup: false,
addPricePopup: false,
isToggleOn: true,
date: props.date || "",
number: props.number || ""
};
}
updateInput = ({ target: { name, value } }) =>
this.setState({ [name]: value });
togglePopup = () => {
this.setState(prevState => ({
showPopup: !prevState.showPopup
}));
};
togglePricePopup = () => {
this.setState(prevState => ({
showPricePopup: !prevState.showPricePopup
}));
};
addPricePopup = () => {
this.setState(prevState => ({
addPricePopup: !prevState.addPricePopup
}));
};
/* adds a new price to the list */
addPrice = newPrice => {
this.setState(prevState => ({
addPricePopup: !prevState.addPricePopup,
// spreads out the previous list and adds the new price with a unique id
priceArr: [...prevState.priceArr, { ...newPrice }]
}));
};
// handlePriceSubmission = () => {
// const { updatePrice } = this.props;
// this.addPricePopup();
// updatePrice(priceArr);
// };
toggleItemEditing = (index) => {
this.setState(prevState => ({
priceArr: prevState.priceArr.map(priceItem => {
// isToggleOn: !state.isToggleOn;
})
}));
};
// toggleItemEditing = index => {
// this.setState({
// items: this.state.items.map((item, itemIndex) => {
// if (itemIndex === index) {
// return {
// ...item,
// isEditing: !item.isEditing
// }
// }
// return item;
// })
// });
// };
render() {
// const { updatePrice } = this.props;
return (
<div className="popup">
<div className="popup-inner">
<div className="price-form">
<h2>Prices</h2>
<div className="scroll-box">
{this.state.priceArr.map((props) => (
<PriceBox
{...props}
key={props.date}
// toggleItemEditing={this.toggleItemEditing()}
onChange={this.handleItemUpdate}
/>
))}
</div>
<div className="buttons-box flex-content-between">
<button
type="button"
onClick={this.addPricePopup}
className="btn add-button">Add +</button>
{this.state.addPricePopup && (
<AddPriceForm
addPrice={this.addPrice}
cancelPopup={this.addPricePopup}
/>
)}
<div className="add-btns">
<button
type="button"
onClick={() => this.props.closeUpdatePopup()}
className="btn cancel-button"
>
Close
</button>
</div>
</div>
</div>
</div>
</div>
);
}
}
The list inside the component above is:
<div className="scroll-box">
{this.state.priceArr.map((props) => (
<PriceBox
{...props}
key={props.date}
// toggleItemEditing={this.toggleItemEditing()}
onChange={this.handleItemUpdate}
/>
))}
</div>
And this is the single list item:
import React, { Component } from "react";
export default class SinglePricebox extends Component {
state = {
showPopup: false, //don't show popup
todaydate: this.props.date
};
/* toggle and close popup edit form window */
togglePopup = () => {
this.setState(prevState => ({
showPopup: !prevState.showPopup
}));
};
toggleEditPriceSubmission = getPriceIndex => {
const { toggleItemEditing, date } = this.props;
// toggle the pop up (close)
// this.showPopup();
toggleItemEditing({ ...getPriceIndex, date });
console.log("date?", date);
};
/* handles edit current security form submissions */
// handleEditSecuritySubmission = editSecurity => {
// const { editCurrentSecurity, id } = this.props;
// // toggle the pop up (close)
// this.togglePopup();
// // sends the editSecurity fields (name, isin, country) + id back to
// // App's "this.editCurrentSecurity"
// editCurrentSecurity({ ...editSecurity, id });
// };
render() {
return (
<div className="pricebox">
<article className="pricetable">
{this.toggleEditPriceSubmission
? "editing" : "not editing"}
<table>
<tbody>
<tr>
<td className="date-width">{this.props.date}</td>
<td className="price-width">{this.props.number}</td>
<td className="editing-btn">
<button
type="button"
className="edit-btn"
onClick={this.toggleEditPriceSubmission}
>
{this.toggleEditPriceSubmission ? "Save" : "Edit"}
</button>
</td>
<td>
<button
type="button"
className="delete-btn">
X
</button>
</td>
</tr>
</tbody>
</table>
</article>
</div>
);
}
}
I have been struggling all afternoon to toggle the edit button in each list item. I was attempting to get the key of each list item which is the this.prop.date.
You can see my code in detail at CodeSandBox: https://codesandbox.io/s/github/kikidesignnet/caissa
I would create a component which will handle the list item as a form and update it as if it was SecurityForm.
{this.state.priceArr.map((props) => {
if(props) {
return <PriceListForm methodToUpdate {...props} />
}else {
retun (
<PriceBox
{...props}
key={props.date}
// toggleItemEditing={this.toggleItemEditing()}
onChange={this.handleItemUpdate}
/>
);
}
})}
and make PriceListForm look like PriceBox but use inputs to capture new data. This way you will have two different components with less complicated logic instead of having a huge component with complex validations to check if you will display an input or not.
create a funtion named Edit to update the State
Edit = (id) => {
this.setState({edit: !this.state.edit, id})
}
and instead of that
<td className="country-width">{this.props.country}</td>
do something like
<td className="country-width">{this.state.edit ? <input type="text" value={this.props.country} onChange={() => UPDATE_THE_CONTENT}/> : this.props.isin}</td>
and call the Edit function onclick of Edit Button and pass ID of that td as a param to update it.
NOTE: by default THE VALUE OF EDIT STATE is false/null.
you can use onChange to update that box or some other techniques like create a button along with it and use that to update it.
hope this might help you
I have a form with one initial empty input field that I want to clone using a Add button and to remove with a Remove one.
As it's not recommended to use index for the keys with dynamic forms, I tried to use uniqid module. But each time the state is updating, keys are renewed and I don't have unique data to identify each input of the form. I can add some items, but can't remove.
input fields have no unique values, no id, how can I do ?
const Form = () => {
const update = e => {};
const items = [{ content: "", color: "" }];
return (
<Fragment>
{items.map((item, idx) => (
<input
htmlFor={`item_${idx}`}
value={item.content}
onChange={update("item", idx)}
/>
))}
<button onClick={e => dispatch(add(idx))}>Add</button>
<button onClick={e => dispatch(remove(idx))}>Remove</button>
</Fragment>
);
You may simply extend your existing items to have unique id property - at its very simplest, you may assign the value of maximum used id increased by 1 to that property - I guess, it'll do the trick for most of practical use cases:
const [inputs, setInputs] = useState([{id:0,value:''}]),
onRowAdd = () => {
const maxId = Math.max(...inputs.map(({id}) => id))
setInputs([...inputs, {id:maxId+1, value:''}])
}
With that, you'll have unique id to anchor to as you delete rows:
onRowRemove = idToDelete => setInputs(inputs.filter(({id}) => id != idToDelete))
Following is the demo of this concept:
const { useState } = React,
{ render } = ReactDOM
const Form = () => {
const [inputs, setInputs] = useState([{id:0,value:''}]),
onInput = (id,value) => {
const inputsCopy = [...inputs],
itemToModify = inputsCopy.find(item => item.id == id)
itemToModify.value = value
setInputs(inputsCopy)
},
onRowAdd = () => {
const maxId = Math.max(...inputs.map(({id}) => id))
setInputs([...inputs, {id:maxId+1, value:''}])
},
onRowRemove = idToDelete => setInputs(inputs.filter(({id}) => id != idToDelete)),
onFormSubmit = e => (e.preventDefault(), console.log(inputs))
return (
<form onSubmit={onFormSubmit} >
{
inputs.map(({id,value}) => (
<div key={id}>
<input
onKeyUp={({target:{value}}) => onInput(id,value)}
/>
<input type="button" onClick={onRowAdd} value="Add" />
<input type="button" onClick={() => onRowRemove(id)} value="Remove" />
</div>
))
}
<input type="submit" value="Log Form Data" />
</form>
)
}
render (
<Form />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
You should create a variable that starts from 0 and adds 1 every time you add a button. That way you will keep track of everyone. Here's an example
let i = 0
const add () => {
//your function to add
i++
//remember i will be the id now
}