Unable to execute onClick function in mapped ellement - javascript

I am trying to add an on click function to my list tags that are mapped from an array, i.e:
{this.props.addresses.map((address, index) =>
<li key={`address-${index}`} onClick={this.addressClick}>{address.prediction}</li>
)}
addressClick is defined in constructor like this:
constructor (props) {
super(props)
this.addressClick = this.addressClick.bind(this)
}
and as a function in the class like this
addressClick () {
console.log('Clicked')
}
When I click on my list tag nothing happens, I don't see any console statements.

Bind this to the map method:
{this.props.addresses.map((address, index) =>
<li key={`address-${index}`} onClick={this.addressClick}>{address.prediction}</li>
).bind(this)}

Suggestion
You can implement this by passing the index to another function and then call addressClick().
function clickListener ( index ) {
var clickedAddress = addresses[index];
addressClick(clickedAddress.prediction);
}
Result:
{this.props.addresses.map((address, index) =>
<li key={`address-${index}`} onClick={`clickListener(${index})`}</li>
)}

Your code should work unless I am missing something. This works for example:
const arr = [1,2,3,4]
class App extends Component {
constructor(props) {
super(props)
this.addressClick = this.addressClick.bind(this)
}
addressClick () {
console.log('Clicked')
}
render() {
return (
<ul>
{arr.map((address, index) =>
<li key={`address-${index}`} onClick={this.addressClick}>{address}</li>
) }
</ul>
)
}
}

Related

TypeError: this is undefined; can't access its "props" property

My todo app goes like this...and..I'm trying to remove the particular todo item out of the list. I'm calling a function from the child component passing another function as a prop. the problem is, whenever I call the function in child component it fails to access the props in the parent component which is also a function. I tried 'bind'ing the map function called in the parent component. But still in vain.
How can I solve this or is there any better way to delete todo-item?? Need help!
Thanks in advance!
class Todo extends Component {
//initializing state
constructor(props) {
super(props);
this.state = {
todoList: ['wash clothes', 'water the garden', 'buy some flowers', 'something else!']
}
}
//rendering and cycling through the data (toodoList)
render() {
var todoList = this.state.todoList;
todoList = todoList.map(function(item, index) {
return(
<TodoItem item={item} onDelete={this.handleClick.bind(this)} key={index} />
);
}, this);
return(
<div className="component-body">
<AddItem />
<ul>
{todoList}
</ul>
</div>
);
}
//removing item
handleClick(item) {
var updatedList = this.state.todoList.filter(function(val, index){
return item !== val;
});
this.setState= {
todoList: updatedList
}
}
}
class TodoItem extends Component {
render() {
return(
<li>
<div>
<span> {this.props.item} </span>
<span className="handle-delete" onClick={this.handleClick}> x </span>
</div>
</li>
);
}
//Custom function
handleClick() {
this.props.onDelete();
}
}
You have to use arrow function
handleClick = () => {
Or if you cant use it,
Define a constructor in the class where the method is, then inside it:
this.handleClick().bind(this)
This way, this you are refering to, and this the method refers to, is the same. Lets say it's miscommunicate between you and the method :)

Call parent component function from child in React.JS

I am trying to make a simple component in React.JS which displays a list of items, then the user can select an item from the list. I am trying to handle the clicks on the list-items by handing down a function from the parent component to the child, so it can notify the parent when it was clicked and the parent can update the selected item. For some reason the function from the child component is not calling the parent function properly as it never gets to the point to write to the console ... I guess it must something to do with binds, but I literally tried every combination possible to make it work.
Tbh, I don't even understand why I have to use "clicked={()=>this.clickedSub}" in the parent component when I already used bind in the constructor, but I guess I don't have to understand everything XD
var months = [
'January','February','March','April','May','June','July','August','September','October','November','December'
];
class SubItem extends React.Component {
constructor(props){
super(props);
this.clickedMe = this.clickedMe.bind(this);
}
clickedMe () {
let i = this.props.id;
console.log("from child: "+i);
this.props.clicked(i);
}
render () {
if (this.props.isSelected) return <a href="#" className="selected" onClick={this.clickedMe}>{this.props.text}</a>;
else return <a href="#" onClick={this.clickedMe}>{this.props.text}</a>;
}
}
class SideMenu extends React.Component {
constructor() {
super();
this.state = {
selected: 0,
open: true
};
this.clickedHead = this.clickedHead.bind(this);
this.clickedSub = this.clickedSub.bind(this);
}
clickedHead () {
this.setState({
open: !this.state.open
});
}
clickedSub(i) {
console.log("from parent:"+i);
this.setState({
selected: i
});
}
render() {
let sel = this.state.selected;
var sublist = this.props.subitems.map(function (item, index){
if (index==sel) return <SubItem text={item} isSelected={true} id={index} clicked={()=>this.clickedSub}/>;
else return <SubItem text={item} isSelected={false} id={index} clicked={()=>this.clickedSub}/>;
});
if (this.state.open) return (
<div className="side_menu">
<div className="menu_item open">
<div className="header" onClick={this.clickedHead}>{this.props.header}</div>
<div className="sub_items">
{sublist}
</div>
</div>
</div>
);
else return(
<div className="side_menu">
<div className="menu_item open">
<div className="header" onClick={this.clickedHead}>{this.props.header}</div>
<div className="sub_items"></div>
</div>
</div>
);
}
}
ReactDOM.render(
<SideMenu header="Month" subitems={months}/>,
document.getElementById('menu')
);
See the Pen vertical collapsible side-menu by Ize8 on CodePen.
Alright, so I struggled with this one for a little while. You have to be really careful when you do NOT use es6 in react. Arrow functions are your friend, and generally just make more sense.
This is where all your trouble is coming from:
var sublist = this.props.subitems.map(function (item, index){
if (index==sel) return <SubItem text={item} isSelected={true} id={index} clicked={()=>this.clickedSub}/>;
else return <SubItem text={item} isSelected={false} id={index} clicked={()=>this.clickedSub}/>;
});
You want to use arrow functions here because you're messing with the scope. You can pass down the function as intended, and you do not have to do this clicked={() => this.clickedSub} syntax which is confusing.
var sublist = this.props.subitems.map((item, index) => {
if (index==sel) return <SubItem text={item} isSelected={true} id={index} clicked={this.clickedSub}/>;
else return <SubItem text={item} isSelected={false} id={index} clicked={this.clickedSub}/>;
});
This will pass down your function as intended, but you have some other issues with your code. It causes an infinite loop, but I'll let you implement this and work through it.
First of all if you don't wont to have .bind(this) in constructor use an arrow function
clickedSub(i){} it is clickedSub = (i)=>{}
Now. I don't get what function you pass to the children. but I will show you an example.
class Parent extends Component {
constructor() {...}
parentFunction = () => {
console.log('This will be called when we click `a` tag in Child component');
}
render() {
return (
<Child funct = {this.parentFunction}/>
)
}
}
class Child extends Component {
handleClick = () => {
this.props.func();
}
render() {
return(
<a onClick={this.handleClick}> Click me </a>
)
}
}

React: how to pass arguments to the callback

I have a list of elements inside my react component, and I want them to be clickable. On click I call some external function passing item ID in arguments:
render () {
return (
<ul>
{this.props.items.map(item => (
<li key={item.id} onClick={() => {doSomething(item.id)}></li>
))}
</ul>
)
}
This code works, but it has a big performance drawback: a lot of new anonymous functions are being created on each call to render.
How can I pass that doSomething function as a reference here while still being able to provide a item.id to it?
You could use data-attributes, to set the correct id on each item while using the same function:
function doSomethingFromEvent(event){
return doSomething(event.target.dataset.id);
}
render () {
return (
<ul>
{this.props.items.map(item => (
<li key={item.id} data-id={item.id} onClick={doSomethingFromEvent}></li>
))}
</ul>
)
}
When setting data-* attributes in your element, you can get it back with dataset, in the form of a hash. For example, in doSomethingFromEvent I have event.target.dataset = {id: *id*}. See more on MDN
This is even cleaner when updating a hash (the state for example), with <li key={item.id} data-myattriute={myvalue} onClick={this.handleClick}></li>, I can simply define handleClick such as:
handleClick(event){
// Here event.target.dataset = {myattribute: myvalue}
Object.assign(myObject, event.target.dataset);
// or
this.setState(event.target.dataset);
}
Coming back to your problem, the great thing with this approach is that if you ensure your container element (ul) cannot be clicked outside its children with data-attributes (li), which is your case, you can declare the function on it:
render () {
return (
<ul onClick={doSomethingFromEvent}>
{this.props.items.map(item => (
<li key={item.id} data-id={item.id}></li>
))}
</ul>
)
}
Now your function is created a single time, and is not even repeated in each item.
What you can do is create a partially applied or higher order function to enclose the item.id and pass it along. So let's look at a toy example of this:
class App {
partiallyApplied = id => e => {
console.log(id,'this is passed in first')
console.log(e,'this is passed in second')
}
render(){
return (
<button onClick={this.partiallyApplied(1234)}>Click Me</button>
)
}
}
Now you have access to 1234 along with your event object
This is use transform-class-properties babel plugin. If do not or cannot use that, you can probably do something like this:
partiallyApplied(id){
return function(e){
console.log(id,'this is id')
console.log(e,'this is event')
}
}
but then you will have to bind this during your call and I just don't like that everywhere.
You could create a new component for every item in the array and use the props, like this:
class Li extends React.Component {
render() {
return <li onClick={this.onClick}> {this.props.children} </li>;
}
onClick = () => {
console.log(this.props.item);
};
}
class App extends React.Component {
state = {
items: [
{id: 1, name: 'one'},
{id: 2, name: 'two'},
{id: 3, name: 'three'},
]
};
render() {
return <ul>
{this.state.items.map(i =>
<Li key={i.id} item={i}>{i.name}</Li>
)}
</ul>;
}
}

React HandleDelete TypeError undefined

I have React component called Websites to handle state
class Websites extends React.Component {
constructor(props) {
super(props);
this.handleWebsiteDelete = this.handleWebsiteDelete.bind(this);
this.state = {
websites: this.props.websites
}
}
handleWebsiteDelete(id) {
console.log("delete")
// How to Run This Function?
// ...further code to delete id from state.websites
}
render () {
return(
<div className="row">
{
this.state.websites.map(function(website, index) {
return (
<WebsiteItem key={website.id} {...website} onDelete={this.handleWebsiteDelete}/>
)
})
}
</div>
);
}
}
Then I have React component called WebsiteItem with a function handleDelete an object:
class WebsiteItem extends React.Component {
handleDelete(e) {
e.preventDefault();
$.ajax({
method: "DELETE",
url: "/websites/" + this.props.id
})
.done(function(){
this.props.onDelete(this.props.id);
}.bind(this))
}
render() {
return (
<div className="card">
{this.props.name}
<a href="#" onClick={this.handleDelete.bind(this)}>Delete</a>
</div>
);
}
}
My goal is to delete a website from a server using ajax inside WebsiteItem component (successfully done) and run a function onDelete inside Websites component to update the state this.state.websites.
I can't manage to run the function with an error: Uncaught TypeError: this.props.onDelete is not a function - I tried to play with bind(this) but not sure if I complete understand it. Thank you.
You almost got it right.
You have to bind the callback function you are passing to this.state.websites.map() to your component instance.
In order to do it, you have to pass the context as the second argument to .map()
{
this.state.websites.map(function(website, index) {
return (
<WebsiteItem key={website.id} {...website} onDelete={this.handleWebsiteDelete}/>
)
},this)
}
Or use arrow functions
{
this.state.websites.map((website, index) => {
return (
<WebsiteItem key={website.id} {...website} onDelete={this.handleWebsiteDelete}/>
)
})
}

React - Child onClick event doesn't trigger when binding parameters

My problem is the following.
I have a "BookShelf" component which contains a "Book" list.
The parent (bookshelf) manage in its state a "selectedBook" property.
When clicking on one child (book), I would like to update its parent selectedBook property.
To achieve this, I use a function defined in the parent properties.
But this method is never trigerred (I tried with a console.log('something') but it never shows.
See my code below :
setSelectedBook(index) {
this.setState({
selectedBook: index
})
},
getInitialState() {
return {
books: [],
selectedBook: null
}
},
componentDidMount() {
let component = this
$.ajax({
url: 'data/books.json',
success (data) {
component.setState({
books: data
})
},
error (err) {
console.log(err)
}
})
},
render() {
let component = this
var bookList = this.state.books.map(function(book, index) {
let selectBook = component.setSelectedBook.bind(component, index)
return (
<Book onClick={selectBook} data={book} key={index} />
)
})
return <div className="book-shelf">
{bookList}
</div>
}
Thanks in advance !
Here is a simple example for you. Also fiddle
You should pass your onClick event as a props to child component, once child component gets it, it will call a callback and pass an id as an agrument to the callback (like i have below).
class Book extends React.Component {
constructor(props){
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.props.click(this.props.id)
}
render(){
return <li onClick={this.handleClick}>{this.props.id} - {this.props.name}</li>
}
}
class BookShelf extends React.Component{
constructor(){
super();
this.onClick = this.onClick.bind(this)
}
onClick(id){
console.log(id)
}
render(){
return <ul> // of course you may use Array.map functions, it's just for example
<Book click={this.onClick} id={1} name={'hello'}/>
<Book click={this.onClick} id={2} name={'world'}/>
</ul>
}
}
React.render(<BookShelf />, document.getElementById('container'));
Also i suggest look at this article Communicate Between Components, it will be useful for you.
Thanks
select method return anonymous function as value.
<Book onClick={this.selectBook(index)} data={book} key={index} />
selectBook (index){
return ((() => {
console.log(" selectBook fired" );
component.setState({selectedBook:index});
}).bind(component,index))
}

Categories

Resources