0

I'm trying to use React Components to render all the projects I have in a .json. This is my code so far:

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      load: '',
      projectInfo:{
        "projects":[]
      }
    }
  }

  getProjectInfo(){
    $.ajax({
      url:'http://localhost:3000/projectInfo.json',
      dataType:'json',
      cache: false,
      success: function(data){
        this.setState({projectInfo: data});
      }.bind(this),
      error: function(xhr, status, err){
        console.log(err);
        alert(err);
      }
    });
  }

  componentDidMount(){
    this.getProjectInfo();
  }

  handleClick = (value) => {
    this.setState({load: value});
  }

  display(app){
    if(app == 'bmi') {
      return <Bmi />
    }
    if (app === 'js') {
      return <Js />
    }
    else return "";
  }

  render() {
    if(this.state.projectInfo){
      var projects = this.state.projectInfo.projects.map(function(project){
        return (<Project id={project.id}
                         projectName={project.name}
                         projectDescription={project.description}
                         imgAddress={project.imgAddress}
                         onClick={this.handleClick}/>)
      })
    }
    return (
      <div className="App">

        <div className="App-header">

          <h2>React Test</h2>

        </div>
        <div className="box">
          {projects}

        </div>
        <div className={this.state.load}>
          {this.display(this.state.load)}
        </div>

      </div>

    );
  }
}

export default App;

I'm having trouble with the onClick, getting the TypeError: Cannot read property 'handleClick' of undefined. I think the problem is that when I use {this.handleClick}, 'this' refers to 'project' passed to the function, but I'm not sure how to get around it.

This was working as desired when I manually listed all the Project Components to be rendered.

LiamG_G
  • 129
  • 1
  • 11

5 Answers5

1

You did not bind the map for the context of this, but since you are using () => notation you can make use of the auto bind e.g.

  var projects = this.state.projectInfo.projects.map(project => {
      return (<Project id={project.id}
                     projectName={project.name}
                     projectDescription={project.description}
                     imgAddress={project.imgAddress}
                     onClick={this.handleClick}/>)
  })

EDIT:

To pass more arguments you have to use a callback for onClick e.g.

 onClick={() => this.handleClick(project.id)}
Murat Karagöz
  • 35,401
  • 16
  • 78
  • 107
  • Thanks :D This has worked to stop the error, although clicking on the component no longer does anything. Probably from how I had it set up initially, rendering the components separately. I would want to pass the id of the Component that is clicked on to render the right thing below, d'you also happen to know if there is a fix for this? – LiamG_G Aug 23 '17 at 12:05
0

Save this.handleClick to a variable before jump into map

if(this.state.projectInfo){
  var handle = this.handleClick
  var projects = this.state.projectInfo.projects.map(function(project){
    return (<Project id={project.id}
                     projectName={project.name}
                     projectDescription={project.description}
                     imgAddress={project.imgAddress}
                     onClick={handle}/>)
  })
}
An Nguyen
  • 1,487
  • 10
  • 21
  • 1
    He already bind the `handleClick` using arrow: `handleClick = (value) => { .. }` – An Nguyen Aug 23 '17 at 11:53
  • @Axnyff 's solution worked to stop the error. However, as with Murat K.'s answer clicking on the component does not do anything. Probably from how I had it set up initially, rendering the components separately. I would want to pass the id of the Component that is clicked on to render the right thing below, d'you also happen to know if there is a fix for this – LiamG_G Aug 23 '17 at 12:08
0

You need to bind to the correct context.Either you can do this

 onClick={this.handleClick.bind(this)}

OR

In constructor

constructor(props){
   super(props);
   this.state = {
     load: '',
     projectInfo:{
      "projects":[]
     }
   };
   this.handleClick = this.handleClick.bind(this);

}
Dev
  • 3,922
  • 3
  • 24
  • 44
  • I still seem to receive the same error when trying these (I'll keep looking over it though, I may have missed something) I thought that using () => for the handleClick would already bind the function? – LiamG_G Aug 23 '17 at 12:06
0

Inside of your constructor bind this to the function as

this.handleClick = this.handleClick.bind(this);
Shubham Jain
  • 930
  • 1
  • 14
  • 24
  • This didn't seem to do the trick, I still receive the same error. I thought that using () => for the handleClick would already bind it? – LiamG_G Aug 23 '17 at 12:05
0

Hi you have problem with context. My suggestion is create proper function to handle click

handleClick(value){
    this.setState({load: value});
 }

than add lambda function to:

if(this.state.projectInfo){
      var projects = this.state.projectInfo.projects.map((project) => {
        return (<Project id={project.id}
                         projectName={project.name}
                         projectDescription={project.description}
                         imgAddress={project.imgAddress}
                         onClick={()=>{this.handleClick(project.id);}}/>)
      })
    }

I am not sure what kind of parameter you need to pass to handle click function but i guess that this is project.id

Ivan Mjartan
  • 1,125
  • 1
  • 12
  • 23