In my react page i'm building a html table. table rows are generated from a child component as follows 'ViewGRNTable'. Upto this page is working great
also i want to get a value from every row in child component and add it to the 'this.state.GRNtotal'. for that I wrote a callback function 'callbackRowSum' which returns 'rowsum' and add value it to this.state.GRNtotal
ViewGRNCartTableRow(){
return this.state.cartProducts.map(function(object,i){
return <ViewGRNTable obj={object} key={i} callbackSum = {this.callbackRowSum} />;
});
}
callbackRowSum = (rowsum) => {
this.setState({GRNtotal: this.state.GRNtotal+rowsum})
}
but it gives an error TypeError: Unable to get property 'callbackRowSum' of undefined or null reference
please help
That's a binding issue, change your function for an arrow function
ViewGRNCartTableRow(){
return this.state.cartProducts.map((object,i) => (
<ViewGRNTable
obj={object}
key={i}
callbackSum={this.callbackRowSum}
/>
));
}
It's because you're not binding the this keyword to the function being passed to map, you can solve that easily with the arrow function or you can manually bind your function like this
ViewGRNCartTableRow(){
return this.state.cartProducts.map(function(object,i){
return (
<ViewGRNTable
obj={object}
key={i}
callbackSum={this.callbackRowSum}
/>
);
}.bind(this));
}
Related
I would like to ask you about using a event function in React.js.
I want to make test function, which would get index and print index when of titlesList is clicked.
But this function doesn't work when is clicked.
Could you give me advices to solve it?
const some = (props) = {
// 'props' have two attributes, titles and contents
// which each is a array of strings.
function test(index){
console.log('clicked');
console.log(index);
}
const titlesList = props.titles.map((title, index) => {
return <div className="eachTitle"
key={index}
onClick={test(index)}>
{title} {index}
</div>
});
return (
<div>
{titlesList}
</div>
);
}
Thank you for reading.
When your component is rendered, it will actually call test(index). This sets the value of onClick to the return value of test(index). What you'll want to do is set onClick to a function that calls whatever you want with the proper arguments.
onClick={() => {test(index)}}
This is an anonymous function, which can be passed around. When clicked, the anonymous function is called, which really just calls test(index) with your arguments. If you didn't need to pass any argument to test, you could have just done:
onClick={test}.
Since you can't tell onClick to pass arguments (besides the event object), the anonymous function is an easy way to get around that.
The problem here is with the binding, there are two approach to resolve it, one mentioned by #Jtcruthers using anonymous function and another way is to create a constructor and register you method using .bind() while calling use this
onClick={this.test(index)}
constructor(props){
super(props);
this.test = this.test.bind(this);
}
function test(index){
console.log('clicked');
console.log(index);
}
const titlesList = props.titles.map((title, index) => {
return <div className="eachTitle"
key={index}
onClick={this.test(index)}>
{title} {index}
</div>
});
I have an API that I'm attempting to filter out every title with a rating of 'Rx'.
The returned json data is stored within a variable called dataItems.
Example: If I want to return the rating for the first object, I'd do dataItems[0].rated
My attempt to filter out anything rated "Rx" is the following.
class AnimeCard extends Component {
render() {
const { dataItems } = this.props
console.log(dataItems)
return (
<AnimeCardWrapper>
{dataItems.filter(item => item !== item.rated['Rx']).map((item, index) => {
return (
<AnimeCardItem>
<PosterImg src={item.image_url} alt="poster" />
<CardTitle key={index}>{item.title}</CardTitle>
<p>{item.score}</p>
<p>{item.rated}</p>
</AnimeCardItem>
)
})}
</AnimeCardWrapper>
)
}
}
Doing so gives me the following error: "TypeError: item.rated is null"
I've attempted to pull out the string "Rx" into a variable and then do rated[rxVariable], but I still receive the same error.
I also attempted to use Object.values(), but I still had no luck. I'm sure there's something I'm overlooking.
Just to be clear, I'd like to display every item except those with rated: "Rx"
I believe you should change the line dataItems.filter(item => item !== item.rated['Rx'] to dataItems.filter(item => item.rated !== 'Rx'
So here's my function:
remove(element)
{
this.setState({ search: this.state.search.filter( item => item !== element ) });
}
I get this error:
Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor).
Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.
If I have it set up like this:
constructor()
{
this.remove = this.remove.bind(this);
}
render()
{
return (
<div>
{ this.state.search.map( (item, index) =>
(
<button key={index} onClick={this.remove(item)}>{item.search}: {item.text}</button>
))
}
</div>
</div>
);
}
But if works fine if I remove the binding (well doesn't really matter) from the constructor and change the button line to this:
<button key={index} onClick={this.remove.bind(this, item)}>{item.search}: {item.text}</button>
So my question is, is there a way to bind it in the constructor so that it can take on the parameter?
The difference between this.remove(item) and this.remove.bind(this, item) is that the first calls the function while the second creates a new function.
So my question is, is there a way to bind it in the constructor so that it can take on the parameter?
You can use this.remove.bind(this, item) and perform the binding the constructor, though it is unnecessary.
If you want to pass item to the event handler, then you have to create a new function in .map that can access item, with your current setup. This can be done via .bind or via a closure. In either case, binding in the constructor is simply not necessary.
You can only avoid creating a new function if provide item in a different way, e.g. wrapping the button with another component that takes item as a prop (therefore pushing the function creation further down):
function Button({item, onClick}) {
return <button onClick={() => onClick(item)}>{item.search}: {item.text}</button>;
}
class Component extends React.Component {
constructor()
{
this.remove = this.remove.bind(this);
}
render()
{
return (
<div>
{ this.state.search.map( (item, index) =>
(
<Button key={index} onClick={this.remove} item={item} />
))
}
</div>
</div>
);
}
}
I'm using React to display a Material Design Grid List of news articles. Im passing the JSON I receive to this GridList component (this.props.newsArticles), and mapping through each returned result so I can filter the data based on whether it has a hi-def image, then sending the data to MediaTile to get rendered. I'm having a problem with my ternary in GridList module's hasHiDefPictures function. I receive a syntax error.
const Components = {
MediaTile: React.createClass({
render: function() {
return (
<GridTile
title={this.props.tile.title}
>
<img src={this.props.tile.multimedia[0].url} />
</GridTile>
)
}
}),
GridList: React.createClass({
render: function() {
var newsArticles = this.props.newsArticles
var renderArticles = newsArticles.data.results.map(function(tile, key) {
return hasHiDefPictures(tile, key)
})
function hasHiDefPictures(tile, key) {
return {tile.multimedia > 3 ? <Components.MediaTile key={key} tile={tile}/> : ""}
};
return (
<div>
<GridList>
{renderArticles}
</GridList>
</div>
);
}
})
}
Now the only way to get around this syntax error is to wrap that returned value in div's like so:
function hasHiDefPictures(tile, key) {
return (
<div>
{tile.multimedia > 3 ? <Components.MediaTile key={key} tile={tile}/> : ""}
</div>
)
};
But I do not want to do that. Does anyone have advice on how to get around this problem? I'm still new to react, so there's a good chance that I'm just not handling the data in the proper place. Any help would be appreciated! Thanks!
You just need to remove the {} around your ternary. {} is useful in JSX (an expression starting with <) to evaluate some JS code, but your array mapping already occur in pure JS code (the beginning of the render function) so it has the regular JS meaning: a block or an object literal.
I am trying to build a todo list in React and Redux.
In my code below, I have a component with a function onDeleteItem. The problem is that I cannot pass onDeleteItem function to the sub-component Item. If I run this code, I get "Cannot read property 'onDeleteItem' of undefined" error.
However, if I try to pass that function outside the map function to a sub-component (to the Item component without mapping the state), it works.
There must be some strange binding problem.
class ItemList extends Component{
constructor(props){
super(props);
}
onDeleteItem(event){
var indexNum = this.props.items.indexOf(document.querySelector(".ion-trash-b").parentElement.textContent)
}
render(){
var items = this.props.items.map(function(todo, index){
return (
<Item
key={index}
todo={todo}
onDeleteItem={this.onDeleteItem.bind(this)} />
)
})
return (
<div>{items}</div>
)
}
}
The problem is, as Borjante mentioned, that the context changes inside the mapping function. this is not pointing to the react component.
You could use an arrow function, however this requires you to transpile the code to ES5 since many major browsers don't support this syntax feature yet.
Instead, you can also bind the context of the mapping function manually, which should also solve your issue:
var items = this.props.items.map(function(todo, index) {
return (
<Item
key={index}
todo={todo}
onDeleteItem={this.onDeleteItem.bind(this)} />
)
}.bind(this))
As the others already pointed out it's all about the context, luckily in ES6 they gave a bit more consistency to "this". I recommend using the new syntax if you have the chance to do so.
render(){
var items = this.props.items.map((todo, index) => {
return (
<Item
key={index}
todo={todo}
onDeleteItem={this.onDeleteItem.bind(this)} />
)
})
return (
<div>{items}</div>
)
}
I think using the ES6 arrow syntax should bind the correct context
i think the problem that you have is related to the this.props.items.map. You map with a regular function, and in that case the this keyword don't reference to the component anymore. If you use a arrow function inside map or you put .bind(this) and the end of the function inside the map. The method most go down the children
You are inside and here this points to the current object in the map.
You can also use fat arrow functions.
class ItemList extends Component{
constructor(props){
super(props);
}
onDeleteItem(event){
var indexNum = this.props.items.indexOf(document.querySelector(".ion-trash-b").parentElement.textContent)
}
render(){
var items = this.props.items.map(function(todo, index){
return (
<Item
key={index}
todo={todo}
onDeleteItem={this.onDeleteItem.bind(this)} />
)
}.bind(this)
return (
<div>{items}</div>
)
}
Noone has mentioned that you are currently creating function per child item while you need single function per ItemList instance. As Official Docs recommend the most performant way is to bind your handlers in constructor.
class ItemList extends Component{
constructor(props){
super(props);
// bind method once per ItemList instance
this.onDeleteItem = this.onDeleteItem.bind(this);
}
render(){
var onDeleteItem = this.onDeleteItem
var items = this.props.items.map(function(todo, index){
return (
<Item
key={index}
todo={todo}
{/* you don't need this here */}
onDeleteItem={onDeleteItem} />
)
})
return (
<div>{items}</div>
)
}
}