How to color specific cells in Table - reactjs

I have a HTML Table in my ReactJS app and I want to color specific cells or rows there. I have my array in state and want to check differences between neighbor rows and then show this differencies by coloring them on red.
class MainTable extends Component {
constructor(props) {
super(props);
this.state = {
results: []
};
}
render() {
const sorted = _.sortBy(this.state.results, ['ORIGIN', 'DATASTAMP']);
return (
<div>
<div>
<Table hover striped bordered responsive size="sm">
<thead>
<tr>
<th>VERSION</th>
<th>DATE</th>
<th>ORIGIN</th>
</tr>
</thead>
<tbody>
{sorted.map(result =>
<tr>
<td>{result.VERSION}</td>
<td>{result.DATASTAMP}</td>
<td>{result.ORIGIN}</td>
</tr>
)}
</tbody>
</Table>
</div>
</div>
);
}
}
I have no idea how to do something like that. Maybe some idea? Sorry for noobie question, I'm new with ReactJS.

To mark some rows you can:
{sorted.map((result, index) =>
<tr className={`item-${index}`}>
<td>{result.VERSION}</td>
<td>{result.DATASTAMP}</td>
<td>{result.ORIGIN}</td>
</tr>
)}
Basically you first need some criteria on which to mark your element, than you can apply a class or style to it. Helpful is classnames so you could do something like that:
{sorted.map((result, index) =>
<tr className={classnames({
even: index % 2 == 0,
odd: !(index % 2 == 0)
})}>
That would add either even or odd to the classes of the <row>, depending on the index in the list.
I guess there are only two things to remember are:
element styles need to objects like: { backgroundColor: #000 } and
css classes need to be added as »className« property

You can use something like
<tr style={{'background-color': result.color}}>...</tr>
or
<tr style={{'background-color': shouldHighlight[key] ? 'red' : 'white'}}>...</tr>
Obviously in second case you need to findout before function, which table rows should be highlighted and store it in the array.
Also, you need to write your map function in format (result, key) => ... or you need to know id of the result.

You can add a flag in each element of your array to indicate if you need to set the background color of your cell or not.
And then use it like this in your render method :
render() {
const sorted = _.sortBy(this.state.results, ['ORIGIN', 'DATASTAMP']);
return (
<div>
<div>
<Table hover striped bordered responsive size="sm">
<thead>
<tr>
<th>VERSION</th>
<th>DATE</th>
<th>ORIGIN</th>
</tr>
</thead>
<tbody>
{sorted.map(result =>
<tr className={(result.colorFlag ? 'colored-background' : '')}>
<td>{result.VERSION}</td>
<td>{result.DATASTAMP}</td>
<td>{result.ORIGIN}</td>
</tr>
)}
</tbody>
</Table>
</div>
</div>
);
And of course, don't forget to create the CSS class
.colored-background {
background-color: #BADA55;
}

Related

How ignore inside map element?

I have been struggling with a problem.
I have multidimensional array and i want to display it to table body.
so i tried to do it with map, however i can't ignore the top of map element, so it displays like nest data.
this is how the multidimensioanl array looks
['2022-05-04', '2022-05-04', '2022-05-04', '2022-05-04', '2022-05-04', '2022-05-04']
['10:11:52', '10:11:52', '10:11:53', '10:11:54', '10:11:55', '10:11:56']
['447', '442', '447', '442', '436', '433']
so i tried to let it looks easy like below.
<tbody>
<tr>
<td>{list[0][0]}</td>
<td>{list[1][0]}</td>
<td>{list[2][0]}</td>
</tr>
<tr>
<td>{list[0][1]}</td>
<td>{list[1][1]}</td>
<td>{list[2][1]}</td>
</tr>
<tr>
<td>{list[0][2]}</td>
<td>{list[1][2]}</td>
<td>{list[2][2]}</td>
</tr>
<tr>
<td>{list[0][3]}</td>
<td>{list[1][3]}</td>
<td>{list[2][3]}</td>
</tr>
<tr>
<td>{list[0][4]}</td>
<td>{list[1][4]}</td>
<td>{list[2][4]}</td>
</tr>
</tbody>
so i tried to use map instead of doing this
const tableData = list.map((data, index) =>
data.map((item, idx) => (
<tr>
<td>{listData[idx]}</td>
</tr>
))
);
return(
<tbody>
{tableData}
</tbody>
)
it doesn't look what i expected.
how can i solve this problem ??
"tr" tag length should be longest
but "td" tag length should be the multidimensioanl array length.
The data, as-is, isn't really suited to .map because you need to iterate over the list[x][y]s, where the x changes, but not the y. Turn the list into a structure where mapping will work first by essentially turning the multidimensional array on its side.
const tableData = list[0].map(
(_, i) => (
<tr>
{
list.map(
listItem => <td>{listItem[i]}</td>
)
}
</tr>
)
);
return (
<tbody>
{tableData}
</tbody>
)

Filtering a table from search word

I have a simple table:
<Table bordered striped>
<thead>
<tr>
<th>Account</th>
<th>Status</th>
<th>Date</th>
<th>Requested By</th>
<th>Approved By</th>
<th></th>
</tr>
</thead>
<tbody>
{accountList?.map((account, index) => (
<tr>
<td>{account.name}</td>
<td>{account.status}</td>
<td>
{account.confirmDate == null
? ""
: moment(account.confirmDate).format("DD-MM-YYYY")}
</td>
<td>{account.requestedBy}</td>
<td>{account.approvedBy}</td>
<td style={{ width: 90 }} className={"text-center"}>
<FontAwesomeIcon
icon={faList}
onClick={() => showAccountModal(account)}
/>{" "}
{/* <FontAwesomeIcon
icon={faCreditCard}
onClick={()=>showBankModal(account)}
/> */}
</td>
</tr>
))}
</tbody>
</Table>
And then i Have a free text search box. I would like to filter all accounts, where name contains the search word. I could fairly easy just have to hooks/variables with the full result in one variable and the filtered in another and then swap between the two. But is there something smarter in REACT? Some kind of filter pipe like Angular? I want to do all filtering client side.
You can use array::filter and filter your state in-line right in the render logic.
({ name }) => !nameFilter || name.includes(nameFilter)
Returns true if the nameFilter is falsey ('', null, undefined, 0) and short-circuit the boolean expression and so will return all elements, otherwise will continue evaluating the expression and process name.includes(nameFilter).
Updated code
accountList?.filter(
({ name }) => !nameFilter || name.includes(nameFilter),
).map((account, index) => (...
If you are looking for an in-box solution use https://react-table.tanstack.com/docs/examples/filtering
But your approach is pretty simple: Store raw data (array) like the source of truth, filtered array store in local state.
Add a handler onChange to input something like this setFilteredData(raw.filter(el => el.name.includes(name)))

React JS: Only map certain elements of an array within JSX tag, where the elements to map are iterated

I have programatically created the above table in React using, the below code:
tableRenderer() {
let table = <Table striped bordered hover responsive="sm" id='mytable'>
<thead>
<tr>
{this.state.headers.map((header, index) =>
<th key={index}>{header} </th>
)}
</tr>
</thead>
{this.state.timeLabels.map((label, index)=>
<tr key={index}> <td><b>{label} </b></td>
{this.state.table.slice(0,4).map((match, index)=>
<td key={index}>{match.teamA} vs {match.teamB}</td>
)}
</tr> )}
</Table>;
However I'm stuck on the last piece of functionality I need: How can I change the value of the slice of the table array, so on the first run it slices 0->number of pitches, second run number of pitches-> number of pitches+number of pitches... etc.
Do I need to create some sot of function that will iterate a variable every time a row is created?
It would be something along the lines of:
{this.state.timeLabels.map((label, index)=> {
const NO_OF_PITCHES = 4;
let from = NO_OF_PITCHES * index;
let to = NO_OF_PITCHES * (index + 1);
return <tr key={index}>
<td><b>{label}</b></td>
{this.state.table.slice(from,to).map((match, index)=>
<td key={index}>{match.teamA} vs {match.teamB}</td>
)}
</tr>)}
} 
that's assuming the slicing is
0,4...
4,8...
8,12...
etc
also you could lift NO_OF_PITCHES to the parent of the map function because it doesn't have to be recreated on each iteration

In react bootstrap how do I show the contents of an array in an html table

I have an array that I add to, each time a button is pressed. That part appears to work. When I output it to a console, I can see that each one is getting added.
const addToPool = () => {
diePool[poolCount] = (
<die
type={diceType}
number={diceNumber}
adjuster={diceAdjuster}
/>
);
poolCount++;
};
How do I loop through and display that in my html table after my form?
return (
<div className="App">
<header className="App-header">
<Form>
<Button onClick={addToPool} aria-controls="diceRoll" aria-expanded={open} variant="secondary" size="sm">
Add to Pool
</Button>
</ButtonToolbar>
</Form>
<Fade in={open}>
<div id="diceRoll">
The result is: {randNum}
</div>
</Fade>
</header>
<Table responsive>
<caption>Dice Pool</caption>
<thead>
<tr>
<th>Type</th>
<th>Number</th>
<th>Adjuster</th>
</tr>
</thead>
<tbody>
</tbody>
</Table>
</div>
);
}
export default App;
In order for React to update the user interface to reflect changes, it expects you to notify it that a change has been made. You do this my modifying the state of your component. In your addToPool method, you are modifying a local variable called diePool. That's just a member variable of the control object, not the state, and React won't know you've made a change.
Instead, modify the state with something like this:
const addToPool(diceType, diceNumber, diceAdjuster) {
this.setState(prevstate => {
return {
diePool: prevstate.diePool.push(
{ type: diceType, number: diceNumber, adjuster: diceAdjuster }
)
}
});
}
For this to work, your component will have to have an initial state to work with. You create this in the constructor:
constructor(props) {
this.state = { diePool: [] };
}
Now, whenever your code calls addToPool, the state will be updated and through setState, React will know about it and call the render method as needed.
In your render method, you will need to consult the state to place the dice rolls in your table. You can do so by using map over your array of dice rolls:
<Table responsive>
// ...snip...
<tbody>
{this.state.diePool.map((roll, index) => {
<tr key={index}>
<td>{roll.type}</td>
<td>{roll.number}</td>
<td>{roll.adjuster}</td>
</tr>
})}
</tbody>
</Table>
(Please note the use of index here to make sure that each table row has a unique index - this is a requirement for React to be able to update table rows individually when it can.)
What's important here is that the component state is a crucial concept in React. If you modify something and you expect React to react (ha!) to it, you'll have to modify the state through setState (not by modifying this.state directly). That's the only way React will know about it. React will then work its magic and call the render methods of your component and its children, as necessary.
inside your body tag, you can iterate through an array using the map method (https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/map)
<tbody>
{dice.map((item,index) => {
return(
<tr key={index}>
<td>{item.key1}</td>
etc...
</tr>
)})}
</tbody>
I figured it out and posting it here in case others have trouble:
declared a counter:
const [poolCount, setPoolCount] = useState(0);
Created my addToPool:
const addToPool = () => {
setDiePool([
...diePool,
{
dp1 : poolCount,
dp2 : diceType,
dp3 : diceNumber,
dp4 : diceAdjuster
}
]);
setPoolCount(poolCount+1);
};
Call my addToPool when I click the button:
<Button onClick={addToPool} aria-controls="diceRoll" aria-expanded={open} variant="secondary" size="sm">
Add to Pool
</Button>
Then create my table and loop:
<Table responsive>
<caption>Dice Pool</caption>
<thead>
<tr>
<th>#</th>
<th>Type</th>
<th>Number</th>
<th>Adjuster</th>
</tr>
</thead>
<tbody>
{diePool.map(die => (<tr key={die.dp1}><td>{die.dp1}</td><td>{die.dp2}</td><td>{die.dp3}</td><td>{die.dp4}</td></tr>))}
</tbody>
</Table>

React: validateDOMNesting: #text cannot appear as a child of <tr>

Can you explain me why react show warning Warning: validateDOMNesting(...): #text cannot appear as a child of <tr>. See Router > RouterContext > CarWashPage > AllCarWashTable > tr > #text.? I don't see any text inside tag tr
Code that renders table
export default class AllCarWashTable extends React.Component{
constructor(props) {
super(props);
this.generateHeaders = this.generateHeaders.bind(this);
this.generateRows = this.generateRows.bind(this);
};
static propTypes = {
cols : React.PropTypes.array.isRequired,
rows : React.PropTypes.array.isRequired
}
generateHeaders() {
let cols = this.props.cols; // [{key, label}]
return cols.map(function(colData) {
return <th key={colData.key}> {colData.label} </th>;
});
}
generateRows() {
let cols = this.props.cols, // [{key, label}]
data = this.props.rows;
if (this.props.rows.length > 0) {
return data.map(function(item) {
var cells = cols.map(function(colData) {
return <td key={colData.key}> {item[colData.key]} </td>;
});
return <tr key={item.id}> {cells} </tr>;
});
}
}
render(){
let headers = this.generateHeaders();
let rows = this.generateRows();
return (
<table className="table table-hove">
<thead>
<tr>
{headers}
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
)
}
}
At the end, my table has the following structure
Where is the problem?
The problem is the spaces in this line:
return <tr key={item.id}> {cells} </tr>;
It might seem silly, but you're actually rendering the cells and some whitespace (i.e. text). It should look like this:
return <tr key={item.id}>{cells}</tr>;
This will also happens when using logical AND short-circuit && to show/hide conditional rows:
{
foo && (<tr><td>{foo}</td></tr>)
}
change it to ternary a ? b : c form where c is null will fix it
{
foo ? (<tr><td>{foo}</td></tr>) : null
}
In my case where was an empty '' output (w\o space inside)
<tbody>
{this.props.orders.map(
order =>this.props.selectedAgent === order.agent ?
<Row item={order} key={ order._id } /> : ''
)
}
</tbody>
The null does the trick:
<tbody>
{this.props.orders.map(
order =>this.props.selectedAgent === order.agent ?
<Row item={order} key={ order._id } /> : null
)
}
</tbody>
The accepted answer wasn't the root cause in my case. I got the same warning when I had a comment after <th> tag. The warning went away when I removed the comment.
const TableHeaders = (props) => (
<tr>
<th>ID</th> {/* TODO: I had a comment like this */}
</tr>
)
EDIT: Removing the space between </th> and {/* will also do the trick.
A <tr> HTML tag indicates a table row. So, any text to be displayed inside a table row must be placed inside <td> HTML tag. This would remove the error.
Example:
return (
<tr>
<td> {/* Using <td> inside <tr> */}
Hello World!
</td>
</tr>
);
Notification warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of <tbody>. Make sure you don't have any extra white space between tags on each line of your source code.
In my case, initialize variable should NOT is null.
let elementCart = ''; {/* in the here,warning will append */}
if(productsCart.length > 0){
elementCart = productsCart.map((item, index) => {
return <CartItem item={item} key={index} index={index} />
});
}
return(
<tbody id="my-cart-body">
{elementCart}
</tbody>
)
Solution: let elementCart = null;
Incase anyone else comes across this error or a similar whitespace error with Material UI in React, my solution after hours of breaking my code was a simple javascript comment inside of my table.
{ /* sortable here */ }
I removed that from between my table elements and the warning disappeared.
Make sure the let variables are valued otherwise initialize a new empty array.
{headers ? headers : []}
or
{rows || []}
For me it works like a charm ...
render(){
let headers = this.generateHeaders();
let rows = this.generateRows();
return (
<table className="table table-hove">
<thead>
<tr>
{headers ? headers : []}
</tr>
</thead>
<tbody>
{rows || []}
</tbody>
</table>
)
}
also || null can solve it.
the important is that the value is not ''
Kevin Law (from other comment) said that you can do this:
{
foo ? (<tr><td>{foo}</td></tr>) : null
}
But you can also fix it like this:
{
Boolean(foo) && <tr><td>{foo}</td></tr>
}
Removing the comment is what helped me too
in my case initialize a variable with null instead of "" works fine
In addition to #Jarno's answer, I also ran into this issue as well. Double check that you don't have any additional } or { at the end of your javascript code:
{this.props.headers.map(header => <th key={header}>{header}</th>)}}
↑
I received this warning when I had a parenthesis instead of a curly bracket
<table>
<tbody>
<tr>
(showMsg && <td>Hi</td>} // leading '(' should be a '{'
</td>
</tbody>
</table>
I received this warning when I put text inside <tr> element with no <td> elements. I wrapped my text with <td> elements and the warning disappeared.
When I did this, having a whitespace in my text or having used {} didn't matter.
In my case I indeed had a <tr> inside a <tr> (intended to use <td>) :)
It's very easy to find. Just open your inspect and look for tag. It should appear at the beginning or at the end of the tag a quoted string like this:
You shouldn't pass an unexpected element in the table body tag. You should use tr and td
In your rows would return the element with tr and td
{rows}
Something like
return(
<tr>
<td>
Hello
</td>
</tr>
)
For my situation, I was getting this error because I forgot to update a SQL query to include an updated column name. The original query was trying to access a column that didn't exist.
This query was being used with Nextjs, React, Material UI and sent to a PostgreSQL server in order to load up a MUI front-end table with database information from the table.
Updating the query fixed the issue.

Resources