Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding onClick for each Component in a .map() function

Tags:

reactjs

I have a created a parent component PicturesFetch that renders another (children) component, Picture which renders a div with a picture inside. Now, to render Picture, PicturesFetch goes through an object (pics) and with a .map() function creates multiple divs with images inside. I want to be able to change the state of each element created separately. Here is what I tried so far, the tested binds were tried one by one, not all together:

PictureFetch.js

class PicturesFetch extends React.Component {
constructor(props) {
    super(props);
        this.state = { 
            isSelected: true,
            pictureClassName: "picture-div green",
            pics : [/*Object witch contains names and src of the images*/]
        };
    this.handlePictureClick = this.handlePictureClick.bind(this); /*#1 tested bind*/
}
handlePictureClick (isSelected){
    if (!isSelected) {
        this.setState({
            isSelected: true,
            pictureClassName: "picture-div green"
        })
    } else {
        this.setState({
            isSelected: false,
            pictureClassName: "picture-div none"
        })
    }
}
render() {
    var changeClass = this.state.pictureClassName;
    var handlePictureClick = this.handlePictureClick.bind(this); /*#2 tested bind*/
    var pics = this.state.pics.map(function(pic, i){
        if (/*something*/) {
            return (
            <div className="pic-div" key={i} >
                <Picture 
                    isSelected={isSelected}
                    pictureClassName={changeClass}
                    onClick={handlePictureClick.bind(this, isSelected)} /*#3 tested bind*/
                />
            </div>
            });
        } 
    return (
    <div className="Options-container">
        <div className="container">{pics}</div>
    </div>
    );
}}

Picture.js

class Picture extends React.Component  {
constructor(props) {
    super(props);
        this.state = { 
            isSelected: true,
            pictureClassName: "picture-div green" 
        };
}
render() {
    var pictureClassName = this.props.pictureClassName;
    return (
        <div onClick={this.props.onClick} className={pictureClassName}>
            <img src={this.props.src} alt={this.props.name} />
            <h5>{this.props.name}</h5>
        </div>
    )
}}

I simplified my code for the example. Now, by placing the bind in positions #1 and #2 changes all the images state together, while in #3 gives me the error Cannot read property 'setState' of undefined.

Which is the correct place to bind the onClick function so I can access each image state separately? Maybe my structure is flawed? Thank you in advance.

like image 693
Yuri Visovsiouk Avatar asked Nov 25 '16 07:11

Yuri Visovsiouk


People also ask

What is the way to bind multiple events in button click?

The first solution to perform multiple onClick events in React is to include all of your actions inside of a function and then call that single function from the onClick event handler. Let's explore how to do that in a React Component: import React from 'react'; function App() { function greeting() { console.

How to call component in React js onClick function?

Example: Updating the State in an onClick Event Handler A very common use of an inline function inside of an onClick event handler in React is to update a component's state. import React, { Component } from 'react'; class App extends Component { constructor(props) { super(props); this.

How to pass a function from one component to another in React?

you can simply pass the state with some name <Button name={stateValue}> and use it in your child component.


2 Answers

You can use either #1 or #3 to bind onClick function. Here in #3 you are missing this in handlePictureClick.

and for the second part accessing each image state separately. you will have to put the isSelected and pictureClassName key inside each pic object. and handlePictureClick can be modified as below.

     handlePictureClick (pic){
          this.setState(pics : this.state.pics.map(function(picture){
              if(picture.id === pic.id){
                if(picture.isSelected){
                   picture.isSelected = false;
                   picture.pictureClassName = "picture-div none";
                } else {
                   picture.isSelected = true;
                   picture.pictureClassName = "picture-div green";
                }

              }
              return picture; 
          ));
    }

  render() {
     var changeClass = this.state.pictureClassName;
        var pics = this.state.pics.map(function(pic, i) {
            if (/*something*/) {
                return (
                <div className="pic-div" key={i} >
                    <Picture 
                        isSelected={pic.isSelected}
                        pictureClassName={changeClass}
                        onClick={this.handlePictureClick.bind(this, pic)} 
                    />
                </div>
                }
            });
        return (
        <div className="Options-container">
            <div className="container">{pics}</div>
        </div>
        );
}
like image 179
duwalanise Avatar answered Oct 13 '22 12:10

duwalanise


Adding to the solution given by @dulwalanise the problem can be solved by bind the map function to the context. this.handlePictureClick is undefined because here this doesn't refer to the context of the component rather the function inside of map

use this

handlePictureClick (pic){
      this.setState(pics : this.state.pics.map(function(picture){
          if(picture.id === pic.id){
            if(picture.isSelected){
               picture.isSelected = false;
               picture.pictureClassName = "picture-div none";
            } else {
               picture.isSelected = true;
               picture.pictureClassName = "picture-div green";
            }

          }
          return picture; 
      ));
}

 render() {
 var changeClass = this.state.pictureClassName;
    var pics = this.state.pics.map(function(pic, i) {
        if (/*something*/) {
            return (
            <div className="pic-div" key={i} >
                <Picture 
                    isSelected={pic.isSelected}
                    pictureClassName={changeClass}
                    onClick={this.handlePictureClick.bind(this, pic)} 
                />
            </div>
            }
        }.bind(this));
    return (
    <div className="Options-container">
        <div className="container">{pics}</div>
    </div>
    );
}
like image 35
Shubham Khatri Avatar answered Oct 13 '22 12:10

Shubham Khatri