I have a React form that currently stores one form fields value in the items array. However, when adding more than one field, I can't get the content of the other fields to be stored in the array as well. It currently stores the value of the First Name input, but can't figure out the Last Name and Phone fields. The data is then rendered to the items array to a 3 column table, but can't get the other fields to show in their respective columns.
Contacts.js
import ContactList from "./ContactList";
class Contacts extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
this.addItem = this.addItem.bind(this);
this.deleteItem = this.deleteItem.bind(this);
}
addItem(e) {
if(this._inputElement.value !== "") {
var newItem = {
firstname: this._inputElement.value,
lastname: this._inputElement2.value,
phonename: this._inputElement3.value,
key: Date.now()
};
this.setState((prevState) => {
return {
items: prevState.items.concat(newItem)
};
});
this._inputElement.value = "";
this._inputElement2.value = "";
this._inputElement3.value = "";
}
console.log(this.state.items);
e.preventDefault();
}
deleteItem(key) {
var filteredItems = this.state.items.filter(function (item) {
return (item.key !== key);
});
this.setState({
items: filteredItems
});
}
render () {
return (
<Panel>
<Tabs onChange={this.onChange} defaultSelectedIndex={0} justified={true}>
<Tab value="pane-1" label="Add Contact" onActive={this.onActive}>
<Form onSubmit={this.addItem}>
<input ref={(a) => this._inputElement = a}
placeholder="First Name" />
<input ref={(a) => this._inputElement2 = a}
placeholder="Last Name" />
<input ref={(a) => this._inputElement3 = a}
placeholder="Phone" />
<Button variant="raised">Add</Button>
</Form>
</Tab>
<Tab value="pane-2" label="List Contacts">
<ContactList entries={this.state.items}
delete={this.deleteItem}/>
</Tab>
</Tabs>
</Panel>
);
}
}
export default Contacts
Contact List
class ContactList extends Component {
constructor(props) {
super(props);
this.createContact = this.createContact.bind(this);
}
delete(key) {
this.props.delete(key);
}
createContact(item) {
return
<tr key={item.key}>
<td onClick={() => this.delete(item.key)}>{item.firstname}</td>,
<td>{item.lastname}</td>
<td>{item.phone}</td>
</tr>
}
render() {
var contactEntries = this.props.entries;
var listItems = contactEntries.map(this.createContact);
return (
<table className="mui-table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<tr>
{listItems}
</tr>
</tbody>
</table>
);
}
};
export default ContactList;
Here is the answer. You are just hitting this._inputElement ref only saving its value whereas in your form you have two more inputs. My suggestion check latest updates react updates. They don't advice you to use "REF" at all.
addItem(e) {
if (this._inputElement.value !== "") {
var newItem = {
firstname: this._inputElement.value,
lastname: this._inputElement2.value,
phonename: this._inputElement3.value,
key: Date.now()
};
this.setState(prevState => {
return {
items: prevState.items.concat(newItem)
};
});
this._inputElement.value = "";
this._inputElement2.value = "";
this._inputElement3.value = "";
}
Related
What I'm trying to pass true/false in another component. The user has the possibility to add languages(max2). When the user adds 2 same languages, the validation error will be shown, which to do that I have used some() method which returns true or false.
I tried it with ```localStorge``, but it didn't work.
import React from "react";
export default class AddLanguage extends React.Component {
constructor(props) {
super(props);
this.state = {
message: "",
items: [],
hasError: ""
};
}
updateMessage(event) {
this.setState({
message: event.target.value
});
}
handleClick() {
var items = this.state.items;
items.push(this.state.message);
this.setState({
items: items,
message: ""
});
}
handleItemChanged(i, event) {
const items = event.target.value;
console.log("items", items);
this.setState((prev) => {
const currList = prev.items;
console.log("currList", currList);
const isDuplicate = currList.some(
(lang, idx) => idx !== i && lang) === items
);
if (isDupliacte) {
return { ...prev, hasError: "A BIG ERROR" };
} else {
currList[i] = items;
return { ...prev, items: currList };
}
});
}
handleItemDeleted(i) {
var items = this.state.items;
items.splice(i, 1);
this.setState({
items: items
});
}
renderRows() {
var context = this;
return this.state.items.map(function (o, i) {
return (
<tr key={"item-" + i}>
<td>
<div>
<input
type="text"
value={o}
autoComplete="off"
onChange={context.handleItemChanged.bind(context, i)}
/>
</div>
{this.state.hasError && (
<div>
<label></label>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
className="inline-block mr-2 h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
</svg>{" "}
{this.state.hasError}
{""}
</div>
</div>
)}
</td>
<td >
<button
type="button"
onClick={context.handleItemDeleted.bind(context, i)}
>
Delete
</button>
</td>
</tr>
);
}, this);
}
render() {
return (
<div>
<div>
<table>
<thead">
<tr>
<th
>
Button
</th>
<th
>
Info
</th>
<th
>
ACTION
</th>
</tr>
</thead>
<tbody>
{this.renderRows()}
</tbody>
</table>
</div>
<div>
<button
className="btn-main"
disabled={this.state.items.length >= 2}
onClick={this.handleClick.bind(this)}
>
<PlusSmIcon />
Add
</button>
</div>
</div>
);
}
}
What I want to pass to the other component is isDuplicate (which might be true/false to another component.
The grid before sorting is as follows:
Now I am going to sort it with Patient ID
Now when I check the Patient ID: 936447 which is in last position after sorting the grid returns to it's original ordering.
But I want the sorted grid even after checking the checkbox/ selecting a row.
This the code for displaying the grid and sorting the grid.
class App extends Component {
constructor(props) {
super(props);
this.state = {
order: {},
orderby: '',
printQueueList: props.printQueueList && props.printQueueList,
};
this.sort = this.sort.bind(this);
}
componentWillReceiveProps(nextProps) {
if (this.state.printQueueList != nextProps.printQueueList)
this.setState({ printQueueList: nextProps.printQueueList && nextProps.printQueueList });
}
sort(event) {
const { order } = this.state;
let { printQueueList } = this.props;
var gridData = printQueueList;
order[event.target.id] = !order[event.target.id];
gridData = _.orderBy(gridData, (o) => typeof o[event.target.id] === 'string' ? o[event.target.id].trim().toLowerCase() : o[event.target.id], order[event.target.id] ? 'asc' : 'desc');
this.setState({
orderby: event.target.id,
printQueueList: gridData,
order
});
}
render() {
return (
<div className="App">
<table >
<thead >
<tr >
<th id="select" >
<Checkbox
input={{
name: 'selectAll',
onChange: //function
value: allSelected
}}
/>
<label htmlFor="SelectAll" >Select All</label>
</th>
<th id="PatientID" onClick={this.sort}>Patient ID {order.PatientID ? <i id="PatientID" className="fa fa-sort-asc" /> : <i id="PatientID" className="fa fa-sort-desc" />}</th>
<th id="DocType" onClick={this.sort}>Type {order.DocType ? <i id="DocType" className="fa fa-sort-asc" /> : <i id="DocType" className="fa fa-sort-desc" />}</th>
</tr>
</thead>
<tbody >
printQueueList && printQueueList.map((Queue, i) => {
return (
<tr key={Queue.PrintQueueID}
onClick={() => onSelectPrintQueueGrid(Queue.PrintQueueID)}>
<td >
<Checkbox
input={{
name: Queue.PrintQueueID,
onChange: //function
value: selectedPrintQueueList.indexOf(Queue.PrintQueueID) !== -1,
}}
/>
</td>
<td className="dashboard_table-cell" title={'Patient ID:' + Queue.PatientID}>{Queue.PatientID}</td>
<td className="dashboard_table-cell" title={'Type:' + Queue.DocType}>{Queue.DocType}></td>
</tr>)
}
)
</tbody>
</table>
</div>
)
}
}
const mapStateToProps = state => {
return {
printQueueList: state.myDashboardReducer.printQueueList,
};
};
const mapDispatchToProps = dispatch => {
return {
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
Since componentWillReceiveProps(nextProps) updates the state with the newProps, the array printQueueList gets updated. For that reason before updating with newProps, I checked whether the array is sorted or not.
If it isn't sorted, update printQueueList with newProps.
componentWillReceiveProps(nextProps) {
if(_.isEmpty(this.state.order)){
if (this.state.printQueueList != nextProps.printQueueList)
this.setState({ printQueueList: nextProps.printQueueList && nextProps.printQueueList });
}
}
I am new to react so bear any nontechnical words.
I have parent component that displays the table headers, now next is child component which has tables' td along with one td is add button when the user clicks on add button. The similar child component should be added as a sibling to previous child component and this process should go on.
Child Component:
class ChildComp extends React.Component{
state = {
Avalue: {value: ''},
Bvalue: {value: ''},
Cvalue: {value: ''},
Dvalue: {value: ''},
Evalue: {value: ''},
Fvalue: {value: ''},
Gvalue: {value: ''},
}
AddanotherSimilarChildcomp=(e)=>{
e.preventDefault();
const historyData = {
A: this.state.A.value,
B:this.state.B.value,
C: this.state.C.value,
D: this.state.D.value,
E: this.state.E.value,
F: this.state.F.value,
G: this.state.G.value,
}
console.log(historyData);
//and should render another similar ChildComp component below the one in which the current ChildComp is present
}
handleChange=(e)=>{
e.preventDefault();
const target = e.target;
const inputName = target.name;
const inputValue = target.value;
this.setState({
[inputName] : {
value: inputValue,
}
});
}
render(){
return(
<tbody id="first-table-row">
<tr>
<td data-th="A"><input type="text" minmax="40" name="A" value={this.state.a.value} onChange={this.handleChange} /></td>
<td data-th="B"><input type="date" minmax="40" name="B" value={this.state.B.value} onChange={this.handleChange} /></td>
<td data-th="C"><input type="text" minmax="225" name="C" value={this.state.C.value} onChange={this.handleChange} /></td>
<td data-th="D"><input type="text" minmax="40" name="D"value={this.state.D.value} onChange={this.handleChange} /></td>
<td data-th="E"><input type="text" minmax="40" name="E" value={this.state.E.value} onChange={this.handleChange} /></td>
<td data-th="F"><input type="text" minmax="40" name="F" value={this.state.F.value} onChange={this.handleChange} /></td>
<td data-th="G">
<div id="samerow">
<span>{this.props.somedata}</span>
<input type="text" minmax="40" name="G"value={this.state.G.value} onChange={this.handleChange} />
</div>
</td>
<td className="save" ><button id="save-btn" onClick={this.AddanotherSimilarChildcomp} type='button'>Add</button></td>
</tr>
</tbody>
)
}
}
Parent Component:
class ParentComponent extends React.PureComponent{
render(){
return(
<div className='table-center' id='table1'>
<table className="rwd-table" id="tblBlah" >
<tbody>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
<th>E</th>
<th>F</th>
<th>G</th>
<th> </th>
</tr>
</tbody>
<ChildComp/>
</table>
</div>
)
}
}
It sounds like you want to clone rows after the button is clicked.. Let me know if this is what you are looking for..
Hope this helps!
class ParentComponent extends React.Component {
render() {
return (
<div>
<table>
<tbody>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
<th>E</th>
<th>F</th>
<th>G</th>
<th> </th>
</tr>
</tbody>
<ChildComp
rows={[
[1, 2, 3, 4, 5, 6, 7],
[8, 9, 10, 11, 12, 13, 14],
[14, 15, 16, 17, 18, 19, 20]
]}
/>
</table>
</div>
);
}
}
class ChildComp extends React.Component {
state = {
tableRows: []
};
componentDidMount() {
this.setState({ tableRows: this.props.rows });
}
addNewRow = (rowToClone, index) => event => {
let newRows = [...this.state.tableRows];
newRows.splice(index, 0, rowToClone.map(i => `${i}` + `${i[0] || i}`));
this.setState({ tableRows: newRows });
};
render() {
return (
<tbody>
{this.state.tableRows.map((row, index) => {
return (
<tr>
{row.map(i => <td>{i}</td>)}
<td>
<button onClick={this.addNewRow(row, index + 1)}>
Clone Row
</button>
</td>
</tr>
);
})}
</tbody>
);
}
}
ReactDOM.render(<ParentComponent />, document.body);
table, th, tr, td {
border: 1px solid black;
}
table {
border-collapse: collapse;
}
td {
width: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
I think you are over-complicating the logic for your component-tree. This is how I interpreted your question.
You have a Parent component that renders a table with headers.
Each Child component is a table row.
You have a button that should add more rows (Child components).
The table cells belonging to each row should be editable (hence
inputs).
See working sandbox: https://codesandbox.io/s/inspiring-wave-bpv7f
It makes the most sense to have the Parent component manage the data-set. So all logic that involves updating or adding something from the data-set should belong to the Parent, not the Child components.
Consider an example like this;
Parent.js
class App extends React.Component {
state = {
headers: ["name", "age", "job", "salary"],
data: [
[
{ name: "Bobby Hill", editting: false },
{ age: 13, editting: false },
{ job: "student", editting: false },
{ salary: 0, editting: false }
]
]
};
createHeaders = () => {
const { headers } = this.state;
return headers.map(item => <th className="table-header">{item}</th>);
};
createBody = () => {
const { data } = this.state;
return data.map((row, index) => (
<Child data={row} rowIndex={index} toggleEdit={this.toggleEdit} />
));
};
addRow = () => {
const { data } = this.state;
const rowTemplate = [
{ name: "", editting: true },
{ age: "", editting: true },
{ job: "", editting: true },
{ salary: "", editting: true }
];
this.setState({
data: [...data, rowTemplate]
});
};
toggleEdit = (rowIndex, cellIndex) => {
const dataCopy = [...this.state.data];
const itemToUpdate = dataCopy[rowIndex][cellIndex];
itemToUpdate.editting = !itemToUpdate.editting;
this.setState({
data: dataCopy
});
};
render() {
return (
<div className="table-center" id="table1">
<table className="rwd-table" id="tblBlah">
<tbody>
<tr>{this.createHeaders()}</tr>
{this.createBody()}
</tbody>
</table>
<button onClick={this.addRow}>Add Row</button>
</div>
);
}
}
In the Parent component we have:
A data-set in our state which is an array of arrays. Each array
pertains to table-row.
When we iterate over the data-set, we pass in an array to each Child
component, which will render the row.
The addRow function will add a new array to our data-set,
containing objects corresponding to each table-header/property.
The toggleEdit function helps us swap between edit modes for each
cell.
Child.js
import React from "react";
const Child = ({ data, rowIndex, toggleEdit }) => {
return (
<tr>
{data.map((cell, cellIndex) => (
<td
className="table-cell"
onDoubleClick={() => toggleEdit(rowIndex, cellIndex)}
>
{cell.editting ? (
<input value={Object.values(cell)[0]} />
) : (
<span>{Object.values(cell)[0]}</span>
)}
</td>
))}
</tr>
);
};
export default Child;
Now in the Child component:
It consumes the data (array) that was passed to it was a prop and
uses it to render the row.
For each item (object) in the array, it creates a table-cell to
display the value.
Double-clicking the cell will toggle the edit property of the item belonging to the parent-state.
If editting is true, than an input is displayed, if not just a
normal table-cell.
I am learning reactjs and going through a trouble to implement update operation. I have added ADD/DELETE but couldn't implement update. When i click on edit button, it appears with the data i clicked on but the problem is, when i submit, the data not adding to my table i submitted but newly added data is adding to table.
only problem with update
here you go for my
form.js file
import React, { Fragment } from "react"
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {name: '', age: '', email: ''};
this.onHandleChange = this.onHandleChange.bind(this);
this.submit = this.submit.bind(this);
}
submit(event, name, age, email) {
if (this.props.submitMe) {
this.props.submitMe(name, age, email);
}
this.setState({name: '', age: '', email: ''}); // clear form after click on submit
}
onHandleChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
componentDidUpdate(prevProps){
if(prevProps.currentEmp != this.props.currentEmp){
this.setState({
index: this.props.currentEmp.index,
name: this.props.currentEmp.name,
age: this.props.currentEmp.age,
email: this.props.currentEmp.email,
});
}
}
render() {
return (
<form>
<div className="form-group">
<input onChange={(event) => this.onHandleChange(event)} value={this.state.name} name="name" type="text" />
</div>
<div className="form-group">
<input onChange={(event) => this.onHandleChange(event)} value={this.state.age} name="age" type="number"/>
</div>
<div className="form-group">
<input onChange={(event) => this.onHandleChange(event)} value={this.state.email} name="email" type="text"/>
</div>
<button onClick={(event) => this.submit(event, this.state.name, this.state.age, this.state.email)} type="button">{this.props.currentButtonName}</button>
</form>
);
}
}
export default Form;
here here you go for my table.js file
import React, {Fragment} from "react"
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
employees: this.props.employees
};
//this.onDelete = this.onDelete.bind(this);
this.onEdit = this.onEdit.bind(this);
}
onEdit(event, index){
if(this.props.editThis){
this.props.editThis(index);
}
}
componentDidUpdate(prevProps){
if(prevProps.employees != this.props.employees){
this.setState({
employees: this.props.employees
});
}
}
render() {
return (
<Fragment>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Age</th>
<th scope="col">Email</th>
<th scope="col">EDIT</th>
<th scope="col">DELETE</th>
</tr>
</thead>
<tbody>
{this.props.employees.map((item, index) => (
<tr key={index}>
<td>{item.name}</td>
<td>{item.age}</td>
<td>{item.email}</td>
<td>
<button
type="button"
onClick={(event) => this.onEdit(event, index)}
className="btn btn-primary btn-sm">EDIT
</button>
</td>
<td>
<button
onClick={(event) => this.props.deleteMe(event, index)}
type="button" className="btn btn-danger btn-sm">DELETE
</button>
</td>
</tr>
))}
</tbody>
</Fragment>
);
}
}
export default Table;
and here you go for my home.js file
import React from "react"
import Table from "./table"
import Form from "./form"
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
current: 'SAVE', // button name
employees: [{name: 'jhon', age: '23', email: 'a#a'}, {name: 'doe', age: '24', email: 'b#a'}],
currentEmp: {},
};
this.onSubmit = this.onSubmit.bind(this);
this.onDelete = this.onDelete.bind(this);
this.setIndex = this.setIndex.bind(this);
}
onSubmit(name, age, email, index=null) {
if(!index && this.state.current == 'SAVE'){
this.setState({ employees: [...this.state.employees, { name: name, age: age, email: email }] });
}
else if(index != null && this.state.current == 'Update'){
var emp = this.state.employees;
emp[index].name = name;
emp[index].age = age;
emp[index].email = email;
this.setState({
currentEmp: {},
employees: emp,
current: 'SAVE'
});
}
else{
this.setState({
currentEmp: {},
current: 'SAVE',
});
}
};
setIndex(index){
var emp = this.state.employees[index];
emp.index = index;
this.setState({
currentEmp: emp,
current: 'Update'
});
}
// delete employee
onDelete(event, index) {
this.setState({
employees: this.state.employees.filter((item, itemIndex) => (index != itemIndex)),
});
};
render() {
return (
<React.Fragment>
<h1>Employee Information System</h1>
<Form currentEmp={this.state.currentEmp} submitMe={this.onSubmit} currentButtonName={this.state.current} />
<hr/>
<table className="table table-striped table-dark">
<Table onUpdateTry={this.edit} editThis={this.setIndex} employees={this.state.employees} deleteMe={this.onDelete} />
</table>
<p className="test">Ignore this please ! Just showed if sass works or not</p>
</React.Fragment>
);
}
}
export default Home;
Can anyone help to fix this? I just screwed up to fix this
Your problem is this line,
else if(index != null && this.state.current == 'Update'){
here you are checking index!=null, but at the time of updation you are never passing any index so your case is false and it will skip this logic.
You just need this condition,
else if(this.state.current == 'Update'){
Also when you call setIndex instead of setting index on actual employee you need to set index in state,
setIndex(index){
var emp = this.state.employees[index];
emp.index = index;
this.setState({
currentEmp: emp,
current: 'Update',
index //set index in state
});
}
And finally your updation logic should be,
else if(this.state.current == 'Update'){
var emp = this.state.employees;
emp[this.state.index].name = name; //use index from state
emp[this.state.index].age = age;
emp[this.state.index].email = email;
this.setState({
currentEmp: {},
employees: emp,
current: 'SAVE'
});
}
Demo
I have A react component which renders a list of items that have been called from an API and set to setOfAllBooks state. Any time I search for the item, setOfAllBooks state is filtered through by the search ternm and the results are held in searchedBooks state. The results of searchedBooks are then passed to Table component and rendered in a list. At this point it works correctly, but when I search for another item it gets clustered in the Table. What I want to do is anytime I search a new Item after I have searched for a previos term I want the list-items in the Table component to be cleared to make way for the new items that have been searched.
import React, { Component } from 'react';
import './Home.css'
import axios from 'axios';
import Autosuggest from 'react-autosuggest';
var books = []
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
return inputLength === 0 ? [] : books.filter(book =>
book.title.toLowerCase().slice(0, inputLength) === inputValue);
};
const getSuggestionValue = suggestion => suggestion.title;
const renderSuggestion = suggestion => (
<div>
{suggestion.title}
</div>
);
const Table = ({ data }) => (
<table class="table table-hover">
<thead>
<tr class="table-primary">
<th scope="col">Title</th>
<th scope="col">Author</th>
<th scope="col">ISBN</th>
<th scope="col">No. Of Copies</th>
</tr>
</thead>
<tbody>
{data.map(row =>
<TableRow row={row} />
)}
</tbody>
</table>
)
const TableRow = ({ row }) => (
<tr class="table-light">
<th scope="row" key={row.title}>{row.title}</th>
<td key={row.author}>{row.author}</td>
<td key={row.isbn}>{row.isbn}</td>
<td key={row.isbn}>24</td>
</tr>
)
class Home extends Component {
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: [],
setOfAllBooks: [],
searchedBooks: []
};
this.searchBook = this.searchBook.bind(this);
}
componentDidMount(){
axios.get('/api/book/viewAll')
.then(res => {
this.setState({ setOfAllBooks: res.data });
books = this.state.setOfAllBooks;
console.log(this.state.setOfAllBooks)
})
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
}
searchBook(event){
event.preventDefault();
this.setState({value: this.state.value});
this.state.searchedBooks = this.state.setOfAllBooks.filter(book => book.title == this.state.value);
this.setState({searchedBook: []})
console.log(this.state.searchedBook);
}
render() {
const { value, suggestions } = this.state;
const inputProps = {
placeholder: 'Enter the name of the book',
value,
onChange: this.onChange
}
return (
<div class="form-group col-lg-4">
<label for="exampleInputEmail1">Email address</label>
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
id="searchFor"
/>
<div className=" form-group">
<label htmlFor="searchFor"> </label>
<button class="form-control btn btn-success" type="submit" onClick={this.searchBook}>Search</button>
</div>
<Table data={this.state.searchedBooks} />
</div>
)
}
}
export default Home;
The results
The Error
You need to add the key prop to the TableRow component as <TableRow key={row.title} row={row} />. Remove the key where you have right now.
.... A good rule of thumb is that elements inside the map() call need keys.
... keys used within arrays should be unique among their siblings. . Doc.
So, it seems title what you used for key will still throw warnings, as they are not uniqe. If you have ID attribute in the row object use that. Adding key to TableRow will remove the first warning, but other warning still be there until title doesn't have the uniq values across all the data.