Warning on bind component on children in ReactJS - javascript

I am getting several Warnings on a Table with controls inside cells. If I click on a cell an event will occur. Some cells have a menu dropdown and an event depending on the selection. I am binding a value to the onClick event so that I can get a value in my function.
I am getting the following warnings on each of my rows:
Warning: bind(): React component methods may only be bound to the component instance.
My code looks like this:
items = this.state.currentValues.map(function(itemVal) {
return (
<tr>
<td onClick={this.rowClick.bind(this, itemVal.OrderId)}>{itemVal.Customer}</td>
<td onClick={this.rowClick.bind(this, itemVal.OrderId)}>{itemVal.OrderId}</td>
<td>
<div> Flag To Group </div>
<div>
<Menu label={itemVal.User}>
<List onSelect={this.assignResponsibleUser.bind(this, itemVal.User)}>
</List>
</Menu>
</div>
</td>
</tr>
);
}.bind(this)
);
return(
<Table selectable={true} scrollable={true} small={true}>
<tr>
{tableHeader}
</tr>
<tbody>
{items}
</tbody>
</Table>
);
I tried replacing:
onClick={this.rowClick.bind(this, itemVal.OrderId)}
with:
onClick={this.rowClick.bind(null, itemVal.OrderId)}
but it gives me the same warning.
I tried removing the .bind(this) but then it gives me an error because it cannot find the rowClick function.
How can I fix this issue?

Instead of using
this.array.map(function(){}.bind(this));
use
this.array.map(function(){}, this);
Its a map option for passing scope.

this.method mustbe a function such as:
senddel:function(id){
this.props.onCommentDel(id);//parent callback
},
render: function() {
return ( < div className = "commentList" >
{this.props.data.map(function(comment){
return (
<Comment key={comment.id} author={comment.author} onCommentDel={this.senddel.bind(this,comment.id)}>
{comment.text}
</Comment>
)
},this)}
< /div>
);
}

Related

Cannot read property 'onRowClickHandler' of undefined

I am pretty new to JS and React-Bootstrap and have read a few other questions related to this error. However, I am still unable to solve my problem. Thankful for any help you can give.
I am trying to implement a row that when clicked on will unhide another row beneath it with more detail about that row. I found this onclick handler from another question, but it was written like
onclickHandler = () => {}
which was giving me babel errors and I can't add that to my package
Here is my code simplified, didn't include the campaign objects, but just assume its a pretty simple json object
class Gateway extends React.Component {
onRowClickHandler(e){
const hiddenElement = e.currentTarget.nextSibling;
hiddenElement.className.indexOf("collapse show") > -1 ?
hiddenElement.classList.remove("show") : hiddenElement.classList.add("show");
}
render() {
function renderTable(element, index) {
return (
<tbody key={element.campaignId}>
<tr onClick={() => this.onRowClickHandler()}>
<td>{element.campaignId}</td>
<td>{element.contextualSignal.value}</td>
<td>{element.bid}</td>
<td>{element.retrievalScore}</td>
</tr>
<tr className="collapse">
<td colSpan="4">
Demo Content1
</td>
</tr>
</tbody>
)} // End of renderTable.
return (
<Table id="retrievalTable" striped bordered hover variant="dark">
<thead>
<tr>
<th>Campaign ID</th>
<th>Contextual Signal Value</th>
<th>Bid</th>
<th>Retrieval Score</th>
</tr>
</thead>
{this.state.campaigns.map(renderTable)}
</Table>
</div>
)}
}
export default Gateway
When I click on a row I get the error
Uncaught TypeError: Cannot read property 'onRowClickHandler' of undefined
the this reference is not referring to your react class but rather to the renderTable function, if you can move it to the class property and bind it using the react class constructor or to the render function
class Gateway extends React.Component {
onRowClickHandler(e){
const hiddenElement = e.currentTarget.nextSibling;
hiddenElement.className.indexOf("collapse show") > -1 ? hiddenElement.classList.remove("show") : hiddenElement.classList.add("show");
}
renderTable(element, index) {
return (
<tbody key={element.campaignId}>
<tr onClick={() => this.onRowClickHandler()}>
<td>{element.campaignId}</td>
<td>{element.contextualSignal.value}</td>
<td>{element.bid}</td>
<td>{element.retrievalScore}</td>
</tr>
<tr className="collapse">
<td colSpan="4">
Demo Content1
</td>
</tr>
</tbody>
)
}
render() {
return (
<Table id="retrievalTable" striped bordered hover variant="dark">
<thead>
<tr>
<th>Campaign ID</th>
<th>Contextual Signal Value</th>
<th>Bid</th>
<th>Retrieval Score</th>
</tr>
</thead>
{this.state.campaigns.map((e, i) => this.renderTable.bind(this, e, i))}
</Table>
</div>
)
}
}
export default Gateway
something like that, don't copy and paste., you probably know the next step
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handlerFunction and pass it to onClick, this will be undefined when the function is actually called.
This is not React-specific behavior; it is a part of how functions work in JavaScript. Generally, if you refer to a method without () after it, such as onClick={this.handlerFunction}, you should bind that method.
Handling Events Reference
There are two ways to deal with this, first is to bind the function under constructor like:
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.onRowClickHandler= this.onRowClickHandler.bind(this);
}
or second is to create an arrow function like:
onRowClickHandler = () => {
// your logic here
}

Ternary operator in map() react

I made an application in React that shows state holidays using state codes. Now I'm wondering how to handle error when a user types in a non-existent state code or more characters. Here's my try to do it.
import React from "react";
const Holidays = props => {
if (props.dataLoaded === false) {
return null;
} else {
return (
<div className="table-responsive">
<table>
<thead>
<tr>
<th>Holiday</th>
<th>Date</th>
<th>Type</th>
</tr>
</thead>
<tbody>
{props.country.map((country, index) =>
country && index === undefined ? (
<p>{props.error}</p>
) : (
<React.Fragment key={index}>
<tr>
<td className="holiday-name">
<strong>{country.name}</strong>
<br />
<p>{country.description}</p>
</td>
<td className="holiday-date">
{country.date.datetime.day}.{country.date.datetime.month}.
{country.date.datetime.year}
</td>
<td className="holiday-type">
{country.type[0]} {country.type[1]}
</td>
</tr>
</React.Fragment>
)
)}
</tbody>
</table>
</div>
);
}
};
export default Holidays;
But with this code when a non-existing state code or more characters is typed in, it throws a error "TypeError: Cannot read property 'map' of undefined".Any help is acceptable
The issue with your code is that your data props.country is being mapped before the data is defined, and as we know the map method .map() can only be used on arrays. Check here for more details. Therefore when you try use map on something that is undefined it will throw that error.
The reason that it is undefined at first is in most scenarios because you are fetching the data from the backend either from a server or from some public API. Before the operation of the data that is being fetched is complete your props.country will be undefined or whatever you initialize it to.
What you should do is change your current code of:
{props.country.map((country, index) =>
country && index === undefined ? (
<p>{props.error}</p>
) : (
<React.Fragment key={index}>
<tr>
<td className="holiday-name">
<strong>{country.name}</strong>
<br />
<p>{country.description}</p>
</td>
<td className="holiday-date">
{country.date.datetime.day}.{country.date.datetime.month}.
{country.date.datetime.year}
</td>
<td className="holiday-type">
{country.type[0]} {country.type[1]}
</td>
</tr>
</React.Fragment>
)
)}
To something like below, where we make a check of your props.country and make sure that it's loaded - once it is, then try use map on it to render out the contents.
{props.country ? props.country.map((country, index) =>
country && index === undefined ? (
<p>{props.error}</p>
) : (
<React.Fragment key={index}>
<tr>
<td className="holiday-name">
<strong>{country.name}</strong>
<br />
<p>{country.description}</p>
</td>
<td className="holiday-date">
{country.date.datetime.day}.{country.date.datetime.month}.
{country.date.datetime.year}
</td>
<td className="holiday-type">
{country.type[0]} {country.type[1]}
</td>
</tr>
</React.Fragment>
)
): <div>Data Loading!</div>}
So what we are doing here is adding another ternary conditional statement to check if props.country is true THEN apply the map method, else you simply return the div with contents "Data Loading!" before the data is loaded.
To clarify we are adding props.country ? right at the start of your code inside the opening curly brace { and : <div>Data Loading!</div> at the end right after the last closing bracket ) and before the closing curly brace }.
Now this <div>Data Loading!</div> could be anything you don't have to put a div or text you could simply put null, but before the data is fetched in those split seconds there will be empty space where your output would be.
Experiment with what you want to put there, but something nice would be to put a loader like a spinner or a styled message to indicate that your data is being fetched.
Edit:
You could also initialize your props.country to an array, so that using .map on it does not resolve into an error automatically even when it's empty. If this was passed down as a props from your parent component's state, simply do:
state = {
country: []
}
The prior methods to solving this problem are better, though. But you should still probably get used to initializing your state properties with the data that they will eventually hold.
import React from "react";
const Holidays = props => {
return (
<div className="table-responsive">
<table>
<thead>
<tr>
<th>Holiday</th>
<th>Date</th>
<th>Type</th>
</tr>
</thead>
<tbody>
{!props.country || props.country.length == 0 ? <p>{props.error}</p> : props.country.map((country, index) =>
<React.Fragment key={index}>
<tr>
<td className="holiday-name">
<strong>{country.name}</strong>
<br />
<p>{country.description}</p>
</td>
<td className="holiday-date">
{country.date.datetime.day}.{country.date.datetime.month}.
{country.date.datetime.year}
</td>
<td className="holiday-type">
{country.type[0]} {country.type[1]}
</td>
</tr>
</React.Fragment>
)}
</tbody>
</table>
</div>
);
};
export default Holidays;

React <div> cannot appear as a child of <tr> Warning

I am getting the following warnings in a React component:
The related code is the following:
import React, { PropTypes } from 'react';
import { Checkbox } from 'react-bootstrap';
const MyComponent = (params) => {
function onSelect(event) {
params.onSelect(event, params.data.id);
}
return (
<tr key={params.data.id}>
{params.isSelectable ? (
<Checkbox onChange={onSelect}>
<td>{params.data.firstName} </td>
<td>{params.data.lastName} </td>
</Checkbox>
) : (
<div>
<td>{params.data.firstName} </td>
<td>{params.data.lastName} </td>
</div>
)}
</tr>
);
};
If I remove the div tags, I get the following error:
Adjacent JSX elements must be wrapped in an enclosing tag
I am new to React, so i am not quite clear on what is going on here. What's the problem and how can I fix it?
Update: my React version is 15.3.2.
If you need to return several elements and can't have a wrapper (such as in this case), you have a new option as of React 16.2. Fragments:
<React.Fragment>
<td>{params.data.firstName} </td>
<td>{params.data.lastName} </td>
</React.Fragment>
Or, you might be able to simplify it as:
<>
<td>{params.data.firstName} </td>
<td>{params.data.lastName} </td>
</>
The fragments won't resolve to any HTML element, so the <td>s will be direct children of your <tr>.
There are two problems with your code
Only td and th are allowed inside tr
In React version < 15, you have to wrap tags in one element, when you try to render them. In React 16, you can now do the following :
[
<td key={1}>{params.data.firstName} </td>,
<td key={2}>{params.data.lastName} </td>
]
instead of wrapping the tds inside a div
I also suggest you extract your logic outside of the tr

How do I wrap a React component that returns multiple table rows and avoid the "<tr> cannot appear as a child of <div>" error?

I have a component called OrderItem that takes an object with multiple objects (at least two) inside it, and renders them as multiple rows inside a table. There will be multiple OrderItem components inside the table. The problem is that in the component's render function, I can't return multiple lines. I can only return a single component, and if I wrap them in a div, it says " <tr> cannot appear as a child of <div>"
The code looks something like this (I left some stuff out for easier readability)
Parent() {
render() {
return (
<table>
<tbody>
{
_.map(this.state.orderItems, (value, key) => {
return <OrderItem value={value} myKey={key}/>
})
}
</tbody>
</table>
)
}
}
class OrderItem extends React.Component {
render() {
return (
<div> // <-- problematic div
<tr key={this.props.myKey}>
<td> Table {this.props.value[0].table}</td>
<td> Item </td>
<td> Option </td>
</tr>
{this.props.value.map((item, index) => {
if (index > 0) { // skip the first element since it's already used above
return (
<tr key={this.props.myKey + index.toString()}>
<td><img src={item.image} alt={item.name} width="50"/> {item.name}</td>
<td>{item.selectedOption}</td>
</tr>
)
}
})}
</div>
)
}
}
Is there a way I can return those multiple rows and have them be in the same table without wrapping them in a div and getting an error? I realize I can make a separate table for each component, but that throws my formatting off a bit.
React 16 is now here to rescue, you can now use React.Fragment to render list of elements without wrapping it into a parent element. You can do something like this:
render() {
return (
<React.Fragment>
<tr>
...
</tr>
</React.Fragment>
);
}
Yes!! It is possible to map items to multiple table rows inside a table. A solution which doesn't throw console errors and semantically is actually correct, is to use a tbody element as the root component and fill with as many rows as required.
items.map(item => (
<tbody>
<tr>...</tr>
<tr>...</tr>
</tbody>
))
The following post deals with the ethical questions about it and explains why yes we can use multiple tbody elements
Can we have multiple <tbody> in same <table>?
One approach is to split OrderItem into two components, moving the rendering logic into a method Parent.renderOrderItems:
class Parent extends React.Component {
renderOrderItems() {
const rows = []
for (let orderItem of this.state.orderItems) {
const values = orderItem.value.slice(0)
const headerValue = values.shift()
rows.push(
<OrderItemHeaderRow table={headerValue.table} key={orderItem.key} />
)
values.forEach((item, index) => {
rows.push(
<OrderItemRow item={item} key={orderItem.key + index.toString()} />
)
})
}
return rows
}
render() {
return (
<table>
<tbody>
{ this.renderOrderItems() }
</tbody>
</table>
)
}
}
class OrderItemHeaderRow extends React.Component {
render() {
return (
<tr>
<td> Table {this.props.table}</td>
<td> Item </td>
<td> Option </td>
</tr>
)
}
}
class OrderItemRow extends React.Component {
render() {
const { item } = this.props
return (
<tr>
<td>
<img src={item.image} alt={item.name} width="50"/>
{item.name}
</td>
<td>
{item.selectedOption}
</td>
</tr>
)
}
}
It seems there is no way to wrap them cleanly, so the easier solution is to just put the whole table in the component and just have multiple tables and figure out the formatting.
Parent() {
render() {
return (
{_.map(this.state.orderItems, (value, key) => {
return <OrderItem value={value} myKey={key} key={key}/>
})}
)
}
}
class OrderItem extends React.Component {
render() {
return (
<table>
<tbody>
<tr>
<td> Table {this.props.value[0].table}</td>
<td> Item </td>
<td> Option </td>
</tr>
{this.props.value.map((item, index) => {
if (index > 0) { // skip the first element since it's already used above
return (
<tr key={this.props.myKey + index.toString()}>
<td> <img src={item.image} alt={item.name} width="50"/> {item.name}</td>
<td>{item.selectedOption}</td>
</tr>
)
}
})}
</tbody>
</table>
)
}
}
It is an old question, but maybe someone stumbles on it. Since I cannot comment yet, here is a little addition to the answer of #trevorgk:
I used this to render a table with multiple rows per item (about 1000 items resulting in about 2000 rows with 15 columns) and noticed really bad performance with Firefox (even in 57).
I had pure components rendering each item (one <body> per item containing two rows each) and each item contained a (controlled) checkbox.
When clicking the checkbox Firefox took more than ten seconds to update - although only one item was actually updated due to pure components. Chrome's update took at most half a second.
I switched to React 16 and I noticed no difference. Then I used the new AWESOME!!! feature of returning an array from a component's render function and got rid of the 1000 <tbody> elements. Chrome's performance was approximately the same while Firefox's "skyrocketed" to about half a second for an update (no perceived difference to Chrome)
In my case, the solution was to return an array instead of a fragment:
return [
<TrHeader />,
<TrRows />
];

Unit Test ReactJS Component yields Invariant Violation: findComponentRoot

I'm starting out with ReactJS and I want to Unit Test it. I made a simple Component which renders an HTML td element:
...
render() {
return (
<td>{this.props.type == 'currency' ? '$' : ''}{this.props.content}</td>
);
}
...
I wrote a Jest Unit Test:
...
it('currency should prepend dollar sign', () => {
const datapointsTd = TestUtils.renderIntoDocument(
<DatapointsTd type="currency" content="123" />
);
const datapointsTdNode = ReactDOM.findDOMNode(datapointsTd);
expect(datapointsTdNode.textContent).toEqual('$123');
});
...
But it fails with the following message:
...
Warning: validateDOMNesting(...): <td> cannot appear as a child of <div>. See di
v > DatapointsTd > td.
FAIL __tests__\DatapointsTd-test.js (49.753s)
- DatapointsTd › it should display content in a td
- Invariant Violation: findComponentRoot(..., .0): Unable to find element. Thi
s probably means the DOM was unexpectedly mutated (e.g., by the browser), usuall
y due to forgetting a <tbody> when using tables, nesting tags like <form>, <p>,
or <a>, or using non-SVG elements in an <svg> parent. Try inspecting the child n
odes of the element with React ID ``.
at invariant (node_modules\react\node_modules\fbjs\lib\invariant.js:39:1
5)
...
I'm not sure what it means, I'm guessing that it tries to put the td element into a div element but then how do people Unit Test a Component like I'm trying to Unit Test?
You are right in guessing so. td must be a child of tr, which in turn must be a child of tbody or thead. The easy way out is to do something like this I guess
const datapointsTd = TestUtils.renderIntoDocument(
<table>
<tbody>
<tr>
<DatapointsTd type="currency" content="123" />
</tr>
</tbody>
</table>
);
Initial Error
I ran into a similar problem trying to test a table header component using React.TestUtils:
var header = TestUtils.renderIntoDocument(
<TableHeader text='Test' />
);
where TableHeader was something like this
class TableHeader extends React.Component {
render() {
return(<th>{this.props.text}</th>);
}
}
This led to the warning <th> cannot appear as a child of <div>.
Attempted Resolution
Attempting to use correct markup led to a new error.
var header = TestUtils.renderIntoDocument(
<table>
<thead>
<tr>
<TableHeader text='Test' />
</tr>
</thead>
</table>
);
The error here was Invariant Violation: findAllInRenderedTree(...): instance must be a composite component
Final Solution
Creating a wrapper component for the test worked for me.
class TestHeader extends React.Component {
render() {
return (
<table>
<thead>
<tr>
<TableHeader text={this.props.text} />
</tr>
</thead>
</table>
)
}
}
var header = TestUtils.renderIntoDocument(
<TestHeader text='Test' />
);
See https://stackoverflow.com/a/40210219/1951290 for the answer that helped me.

Categories