0

I have a component which displays images on click of a button. These images when get displayed on the screen, I want to apply an onClick listener to them such that the image that I click on gets displayed on the whole screen.

Code:

class App extends React.Component{
  constructor(props){
    super(props);
    this.state={
      images:[],
      pano:'',
      name:'',
      list:[]
    }
    this.loadImages=this.loadImages.bind(this);
    this.loadOne=this.loadOne.bind(this);
  }


  loadImages(){
    console.log("load");
    var that=this;
    $.ajax({
      type:'GET',
      url:'https://demo0813639.mockable.io/getPanos',
      success:function(result){
        var images=that.state.images;
        for(var i=0;i<result.length;i++){
          that.state.images.push({"pano":result[i].pano,"name":result[i].name});
        }
        that.setState({
          images:images
        })
      }
    })
  }

  loadOne(url){
     console.log("hi")
  }


  render(){  
    var list=this.state.list;
    list=this.state.images.map(function(result){
      //console.log(result.name);
      return(<div className="box">
        <div className="label">{result.name}</div>
            <img src={result.pano} className="image"/>   
        </div>
      )
    })
    return( 
      <div>
        <button onClick={this.loadImages}>Click</button>
        <div onClick={this.loadOne(this,this.props.result.pano)}>{list}</div>      
      </div>
    );
  }
}

loadOne() is the function which gets called after clicking on an image. I get the error:

cannot read property pano of undefined

And if I do like this:

render(){
  var list=this.state.list;
  list=this.state.images.map(function(result){
    return(<div className="box">
        <div className="label">{result.name}</div>
          <img src={result.pano} className="image" onClick={this.loadOne(this,this.props.result.pano)}/>   
      </div>
    )
  })

  return( 
    <div>
      <button onClick={this.loadImages}>Click</button>
      <div >{list}</div>      
    </div>
  );
}

then I get the error:

cannot read property loadOne of undefined.

So, how can I pass specific image url to my function?

5
  • onClick={() => this.loadOne(this,this.props.result.pano)}. You need to pass a function instead of what you're doing now which is to immediately invoke it during render. I'm sure there's a duplicate for this somewhere, will try to find it. Commented Aug 1, 2017 at 10:16
  • ^ accepted answer on that also suggest a cleaner approach with sub-components. Commented Aug 1, 2017 at 10:17
  • Using the above suggestion also, I get cannot read property loadOne of undefined. @ivarni Commented Aug 1, 2017 at 10:18
  • @Aayushi you forgot to use bind word: onClick={this.loadOne.bind(this,this.props.result.pano)} Commented Aug 1, 2017 at 10:20
  • even with the bind keyword, I get the same error @MayankShukla Commented Aug 1, 2017 at 10:22

2 Answers 2

4

Issue with second snippet is:

1- You forgot to bind the map callback method, use arrow function:

var list = this.state.list;
list = this.state.images.map((result) => {   //here
    .....

2- You missed the bind word here:

onClick = {this.loadOne.bind(this,result.pano)}

3- Instead of this.props.result.pano it should be result.pano.

Full code:

var list = this.state.list;
list = this.state.images.map((result) => {
    return(
        <div className="box">
            <div className="label">{result.name}</div>
            <img src={result.pano} className="image" onClick={this.loadOne.bind(this,result.pano)}/>   
        </div>
    )
})

Working Code:

class App extends React.Component{
  constructor(props){
    super(props);
    this.state={
      images:[],
      pano:'',
      name:'',
      list:[]
    }
    this.loadImages=this.loadImages.bind(this);
    this.loadOne=this.loadOne.bind(this);
  }
 
 
  loadImages(){
    var that=this;
    $.ajax({
      type:'GET',
      url:'https://demo0813639.mockable.io/getPanos',
      success:function(result){
        var images=that.state.images;
        for(var i=0;i<result.length;i++){
          that.state.images.push({"pano":result[i].pano,"name":result[i].name});
        }
        that.setState({
          images:images
        })
      } 
    })
  }
 
  loadOne(pano){
    console.log('pano', pano);
  }
 
 
  render(){
    var list = this.state.list;
    list = this.state.images.map((result)=>{
    return(
        <div className="box">
          <div className="label">{result.name}</div>
            <img src={result.pano} className="image" onClick={this.loadOne.bind(this,result.pano)}/>  
        </div>
      )
    })
    return(
      <div>
        <button onClick={this.loadImages}>Click</button>
        <div >{list}</div>      
      </div>
    );
  }
}
 
ReactDOM.render(<App/>,document.getElementById('container'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id='container'/>

Check this answer: Why is JavaScript bind() necessary?

Sign up to request clarification or add additional context in comments.

6 Comments

cannot read property pano of undefined I am getting after making the changes
@Aayushi reason is, you are using this.props.result.pano in loadOne function it should be result.pano. check the updated answer.
not this.result.pano, it should be result.pano check the full code and use the same it will work :)
It becomes non-clickable after result.pano. And isn't everything supposed to be bind to this keyword in react?
check the working code in updated code, that was a typo replace loadOne method with loadOne(pano){ console.log('pano', pano); }
|
0

You need to bind you click function to that image.

    class App extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                images: [],
                pano: '',
                name: '',
                list: []
            }
            this.loadImages = this.loadImages.bind(this);
            //The loadOne cant be bound to this here, it needs a 
            //bind for each image. Effectivly creating one version of loadOne per image.  
        }

        loadImages() {
            $.ajax({
                type: 'GET',
                url: 'https://demo0813639.mockable.io/getPanos',
                success:(result) => {
                    var images = this.state.images;
                    for (var i = 0; i < result.length; i++) {
                        this.state.images.push({ url: result[i].pano, name: result[i].name });
                    }
                    this.setState({
                        images: images
                    })
                }

            })
        }

        loadOne(url, mouseEvent) {
          console.log(url);
        } 


        render() {
            //This can get extracted to a custom component
            var imagesList = this.state.images.map((image, i) => {
                return (<div key={i} className="box" >
                    <div className="label">{image.name}</div>
                    {/*This is where the bind/curry should be */}
                    <img onClick={this.loadOne.bind(this, image.url)} src={image.url} className="image" />
                </div>
                )
            });

            return (
                <div>
                    <button onClick={this.loadImages}>Click</button>
                    <div>{imagesList}</div>
                </div>
            );
        }
    }

ReactDOM.render(<App />, document.getElementById('container'));

Your clickhandler (loadOne) will then be called with the result.pano and the event as second argument passed in by react.

http://jsbin.com/zokugitopa/1/edit?js,output

2 Comments

where exactly? ?
Oh i miss read the question sample. Give me a second and ill clarify.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.