Removing element from table reactjs - javascript

I have created the table and I have different types of elements that I want to add. After adding the element I want the option to delete one specific element by clicking on the remove button.
import * as React from 'react';
import { useState } from 'react';
import { Table } from 'reactstrap';
import { Button } from 'reactstrap';
export default function App() {
let [myArray, updateMyArray] = useState([]);
function addElement() {
updateMyArray((arr) => [
...arr,
<Element value={myArray.length} handleDelete={handleDelete} />,
]);
}
const handleDelete = (index, e) => {
updateMyArray(myArray.filter((item) => item.value !== e.target.value));
};
return (
<div>
<Button onClick={addElement}>Add Element</Button>
<Table>
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>{myArray}</tbody>
</Table>
</div>
);
}
function Element({ value, handleDelete }) {
return (
<tr>
<td>{value}</td>
<td>Test</td>
<td>
<Button onClick={(e) => handleDelete(value, e)}>Remove Element</Button>
</td>
</tr>
);
}
With this code snippet, I have an option to create as many elements as I want. But if I create, let's say 10 elements, and I want to delete the 5th element, one cannot delete only that element, but all elements added after the 5th element is created. I am new at the react therefore I cannot realize what causes this problem.
The link for the code: https://stackblitz.com/edit/react-ts-7mwhmw?file=App.tsx

You should create unique index each element of array and dont store component as #Chrisg mentioned.
solution:
let [myArray, updateMyArray] = useState([]);
function addElement(value) {
updateMyArray((arr) => [...arr,value]);
}
const handleDelete = (index) => {
updateMyArray(myArray.filter((item,i) => index !== i));
};
return (
<div>
<Button onClick={() => addElement("newElement")}>Add Element</Button>
<Table>
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>{myArray.map((item,index) => {
return <tr>
<td>{item} {index}</td>
<td>Test</td>
<td>
<Button onClick={() => handleDelete(index, item)}>Remove Element</Button>
</td>
</tr>
})}</tbody>
</Table>
</div>
);
link : https://stackblitz.com/edit/react-ts-wqsmfq?file=App.tsx

Related

ReactJS add table rows

I want an app.js that imports a component, that generates a row in a table for each element in an array.
import myRows from './myRows';
const myApp = ({ values}) => {
return (
<div>
<div>
{values.map((entry, index) => {
return <myRows entry={entry} key={index}/>
})}
</div>
</div>
)
}
...
const myRows = ({ entry}) => {
return (
**ROW ENTRY like row name = entry.name**
)
}
...
Maybe I should create in the my App.js a table and create in the map function a row for each entry in values? Idk how to do that.
I want a simple table where each row is created with each entry in values
My idea is not working and mybe it is stupid and there are many better ways..
import myRows from './myRows';
const myApp = ({ values}) => {
return (
<table>
<thead>
<tr>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th>7</th>
</tr>
</thead>
<tbody>
{values.map((entry, index) => {
return <myRows entry={entry} key={index}/>
})}
</tbody>
</table>
)
}
...
const myRows = ({ entry}) => {
return (
<tr>
<th>entry.name</th>
<td>entry.secondName</td>
<td>C</td>
<td>D1</td>
<td>entry.value22</td>
<td>E3</td>
<td>E4/</td>
</tr>
)
}
...
Don't use index as key. Each entry should have a unique identifier; either an id or you can interpolate some values to create a unique key for each row (e.g key={${row.name}_${row.createdAt}}
const App = ({ array }) => {
return <table>
<tbody>
{ array.map(row => <Row data={row} key={row.id} />) }
</tbody>
</table>
}
const Row = ({ data }) => {
return <tr>
<td>{data.name}</td>
<td>{data.created}</td>
...
</tr>
}

How do i highlight a row based on another row condition in react jsx?

I need to highlight rows in red color if the value of the ratio (column) is equal to or greater than 0.96. I was not sure where to add the lines to make these changes. Can anyone help me with this? I am trying to highlight the rows with red color where the condition satisfies. i.e. if the ratio value is greater than or equal to 0.96.
Here is the code I have:
import React, { useState, useContext } from "react";
import { AppContext } from "../../context/AppContext";
import { getCostSales} from "../../API/api";
import Button from "react-bootstrap/Button";
import Table from "react-bootstrap/Table";
const CostSales = () => {
const {
userRole,
employee,
setUserRole,
setEmployee,
isLoggedIn,
setIsLoggedIn
} = useContext(AppContext);
const [tableData, setTableData] = useState([]);
// TODO: conditional render for table? or can show table headers at least?
const onHandleRunCostSales = () => {
//call API/api method here
console.log("Run below cost sales report clicked:");
getCostSales().then((res) => {
if (res) {
setTableData(res);
console.log("res in below cost sales report: ", res);
}
});
};
return (
<div>
{userRole === "Manager" || userRole === "Owner" ? (
<div>
<Button variant="primary" onClick={onHandleRunCostSales}>
Run Report
</Button>
{tableData ? (
<div>
<Table>
<thead>
<tr>
<th>Vin</th>
<th>Date</th>
<th>Invoice Price</th>
<th>Sold Price</th>
<th>Ratio</th>
<th>Customer Name</th>
<th>SalesPerson</th>
</tr>
</thead>
<tbody>
{tableData.map((rowData, index) => (
<tr>
<td>{rowData.vin}</td>
<td>{rowData.date}</td>
<td> {rowData.invoice_price} </td>
<td>{rowData.sold_price}</td>
<td>{rowData.ratio}</td>
<td>{rowData.customer_name}</td>
<td>{rowData.salesperson}</td>
</tr>
))}
</tbody>
</Table>
</div>
) : (
<div>
<p>No data available for report</p>
</div>
)}
</div>
) : (
<div>
<p>Sorry, we can't show you this page</p>
</div>
)}
</div>
);
};
export default CostSales;
You can add the conditional style for tr element based on ratio
Update: you can have util method to decide on color based on ration
function highlightColor(ratio) {
if (ratio > 0.98) {
return "green";
} else if (ratio >= 0.96) {
return "red";
}
return "white";
}
<tbody>
{tableData.map((rowData, index) => (
<tr style={{ backgroundColor: highlightColor(rowData.ratio) }}>
<td>{rowData.vin}</td>
<td>{rowData.date}</td>
<td> {rowData.invoice_price} </td>
<td>{rowData.sold_price}</td>
<td>{rowData.ratio}</td>
<td>{rowData.customer_name}</td>
<td>{rowData.salesperson}</td>
</tr>
))}
</tbody>;

How to get text element of the given react component in React.cloneElement?

Developers give me the headings of the table:
const CustomersTable = () => {
var headers=<>
<th>Name</th>
<th>Age</th>
<th>Another text</th>
</>
return <Table
headers={headers}
/>
}
And this is the code of the Table component:
const Table = ({headers}) => {
var clonedHeaders = React.Children
.toArray(headers.props.children)
.map(header => React.cloneElement(header, {
className: "text-gray-900 py-3 font-light text-xs"
}));
return <table>
<thead>
<tr>
{clonedHeaders}
</tr>
</thead>
</table>
}
I can use React.cloneElement to add attributes to the elements I receive as props of my component.
However, I want to be able to change the text content of those received elements too.
For example, I want to call my locale translation function on table header elements, automatically. Right now, if developers want to make their tables multi-lingual, they should write this:
var headers = <>
<th>{t('Name')}</th>
<th>{t('Age')}</th>
<th>{t('Other text')}</th>
</>
I want to centralize that t(text) function for all headers prop. Can I do that?
You can use the same technique on the child elements of the headers as you do on the headers themselves:
const clonedHeaders = React.Children
.toArray(headers.props.children)
.map(header => React.cloneElement(header, {
className: "text-gray-900 py-3 font-light text-xs",
children: React.Children.toArray(header.props.children).map(child => {
return typeof child === "string" ? t(child) : child;
})
}));
Live Example:
const {useState} = React;
function t(english) {
// Just so we can see that it happens
return english.toLocaleUpperCase();
}
const CustomersTable = () => {
var headers=<React.Fragment>
<th>Name</th>
<th>Age</th>
<th>Another text</th>
</React.Fragment>;
return <Table
headers={headers}
/>;
};
const Table = ({headers}) => {
const clonedHeaders = React.Children
.toArray(headers.props.children)
.map(header => React.cloneElement(header, {
className: "text-gray-900 py-3 font-light text-xs",
children: React.Children.toArray(header.props.children).map(child => {
return typeof child === "string" ? t(child) : child;
})
}));
return <table>
<thead>
<tr>
{clonedHeaders}
</tr>
</thead>
</table>;
};
ReactDOM.render(<CustomersTable />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
That example doesn't do any recursion, so it won't handle <th><span className="something">Name</span></th>. If you want to handle that, you'll have to write a recursive function to handle it, but it'll be along the same lines.

How to filter through a table with React?

I am trying to filter through an array so that when a certain tab is clicked, only those results show. I have managed to isolate the certain variables I want to remain but the others that don't fit the criteria still remain. How do I get the filter method to actually render on the page so that the results can be shown. I have searched for hours for and have tried to get the tbody and sort through that but I just get confused as I am mostly new to javascript and react. Can someone point me in the right direction?
Filter Method
const tbody = document.getElementsByTagName('tbody')
console.log(tbody)
//change active class
function addTabBackground() {
const tabs = document.querySelectorAll('[data-tab]')
window.onload = function () {
tabs.forEach(tab => {
tab.addEventListener('click', () => {
if (tab.getAttribute('data-tab') === 'gains') {
listOfOptions.map(option => {
console.log(option.totalProfit)
})
}
tabs.forEach(tab => {
tab.classList.remove('active')
})
tab.classList.add('active')
})
})
}
}
<div className="outputs" >
<table>
<thead>
<tr>
<th>Date</th>
<th>Stock Name</th>
<th>Price Of Option</th>
<th>Number Of Options</th>
<th>Total Amount Spent</th>
<th>Option Sold At</th>
<th>Amount Of Options Sold</th>
<th>Proft</th>
</tr>
</thead>
{listOfOptions.map(option => {
return (
<tbody>
<tr>
<td>{option.clock}</td>
<td>{option.name.toUpperCase()}</td>
<td>${option.price}</td>
<td>{option.amountOfOptions}</td>
<td>${option.totalAmountSpent.toFixed(2)}</td>
<td>${option.optionPriceSoldAt}</td>
<td>{option.amountOfOptionsSold}</td>
<td style={{ color: option.totalProfit >= 0 ? 'green' : 'red' }}>${option.totalProfit.toFixed(2)}</td>
</tr>
</tbody>
)
})}
</table>
</div>
I have used React-Bootstrap-v5 to get Nav, Nav.Item with eventKey and then passed selectedKey in its onSelect function to change the tabs.
Then once we get the data inside my data variable, I used the map function to go over the array. Inside Map Function I have used the required condition to filter the elements i.e. variable = 'Open' or 'Live'.
This will only show the Open type in Open Tab and Live Type Data inside Live Tab.
Hope it's clear to you.
import React, { useEffect, useState } from 'react';
const TestSeries = () => {
// Declare Variable for data to be fetched from API
const [data, setData] = useState([]);
const fetchTestData = async () => {
const response = await axios.get(site_ip + '/apiLink');
setData(response.data.Content);
};
useEffect(() => {
fetchTestData();
}, []);
// State Variable to keep track of active tab
const [activeTab, setActiveTab] = useState('Open');
return (
<>
<Nav
activeKey={activeTab}
fill
variant="pills"
onSelect={(selectedKey) => {
setActiveTab(selectedKey);
}}
>
<Nav.Item>
<Nav.Link eventKey="Open">Open</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="Live">Live</Nav.Link>
</Nav.Item>
</Nav>
<br />
<Table striped bordered hover>
<thead>
<tr>
<th>#</th>
<th>Column1</th>
<th>Column2</th
<th>Column3</th>
<th>Column4</th>
<th>Data Type</th>
</tr>
</thead>
<tbody>
{data.map((item, index) =>
// Condition by which data will be filtered
item.data_type == activeTab ? (
<tr>
<td>{index + 1}</td>
<td>{item.col1}</td>
<td>{item.col2}</td>
<td>{item.col3} </td>
<td>{item.col4}</td>
<td>{item.data_type}</td>
</tr>
) : null
)}
</tbody>
</Table>
</>
);
};
export default TestSeries;
Result
Assuming from the comment that you have something vaguely looking like:
function Page() {
return (
<>
<Navbar />
<Table />
</>
)
}
What you need to do is to store the current tab in a state, and pass this state down to the Table component so that you can use a Array.filter when rendering your table.
function Page() {
const [activeTab, setActiveTab] = useState(DEFAULT_ACTIVE_TAB)
return (
<>
<Navbar activeTab={activeTab} setActiveTab={setActiveTab} />
<Table activeTab={activeTab} />
</>
)
}
Your Navbar component should have a onClick handler where it is calling the setActiveTab function when the active tab change.
Then in your Table component you should have something like this:
function Table({ activeTab }) {
return (
<table>
...
{listOfOptions
.filter(option => /* something depending on the activeTab */)
.map(option => <... />)
}
</table>
}

How to filter array of date onClick outside map function?

I'm finally starting to understand how to pass and retrieve data using React. But I have one problem, I have this click handler this.SortASC when I click on the title I would like to sort titles based on alphabetic order.
I'm struggling to get this to work.. any idea how i can fix this?
Thanks in advance.
My code:
import React, { Component } from 'react';
import { getMovies } from '../services/fakeMovieService';
class Movies extends Component {
state = {
movies: getMovies(),
};
handleDelete = movie => {
const updateMovies = this.state.movies.filter(m => m._id !== movie._id); // Display all movies but not the one selected.
this.setState({
movies: updateMovies,
});
};
SortASC = () => {
console.log('Sorted');
};
render() {
return (
<React.Fragment>
{this.state.movies.length > 0 ? (
<div className="m-2">
<p>
Showing {this.state.movies.length} in the database.
</p>
<table className="table table-striped table-dark">
<thead>
<tr>
<th scope="col" onClick={this.SortASC}>
Title
</th>
<th scope="col">Genre</th>
<th scope="col">Stock</th>
<th scope="col">Rate</th>
<th scope="col"> </th>
</tr>
</thead>
<tbody>
{this.state.movies.map(movie => {
const {
_id,
title,
genre,
numberInStock,
dailyRentalRate,
} = movie;
return (
<tr key={_id}>
<th>{title}</th>
<td>{genre.name}</td>
<td>{numberInStock}</td>
<td>{dailyRentalRate}</td>
<td>
<button
onClick={() =>
this.handleDelete(movie)
}
className="btn btn-danger btn-sm">
Delete
</button>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
) : (
<h4 className="m-4">
There are no movies in the database.
</h4>
)}
</React.Fragment>
);
}
}
export default Movies;
You can just sort the movies array in state in SortASC.
Like this:
SortASC = () => {
const { movies } = this.state;
this.setState({
movies: movies.sort((a, b) => a.title > b.title ? 1 : -1)
})
};
And if you want to sort descending, you can swap the 1 and -1.
you want something like: arr.sort((titleA, titleB) => titleA - titleB)
although it can get a little more complex (checking for same/duplicate titles, etc)

Categories