nachomacaso
nachomacaso

Reputation: 35

Cannot read property 'handleClick' of undefined

I am new to React and I can't seem to figure this out. I am trying to create dynamic tabs that will display a tab's content when clicked. I am getting a "cannot read property 'handleClick' of undefined" error in the console. Here's what I have so far:

<script type="text/babel">
  class App extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        tabs: [
          {id: 0, name: 'Foo'},
          {id: 1, name: 'Bar'},
          {id: 2, name: 'Fizz'}
        ],
        contents: [
          {name: 'Foo', content: 'Hello'},
          {name: 'Bar', content: 'World'},
          {name: 'Fizz', content: 'Buzz'},
        ]
      };
      this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
      console.log('Hi');
    }
    renderTab(tab) {
      return(<Tab key={tab.id}
                  value={tab.name}
                  onClick={() => this.handleClick()}
                  />
      );
    }
    renderContent(i) {
       return(<Content content={this.state.contents}/> );
    }
    render() {
      return(
        <div className="container-tabs">
          <div className="tab">
            {this.state.tabs.map(this.renderTab)}
          </div>
          <div className="tabcontent">
            <p>{this.renderContent}</p>
          </div>
        </div>
      ) ;
    }
  }

  class Tab extends React.Component {
    constructor(props) {
      super(props);
    }
    render() {
      return (
        <button className="tablinks"
                onClick={() => this.props.onClick()}>
          {this.props.value}
        </button>
      );
    }
  }

  class Content extends React.Component {
    constructor(props) {
      super(props);
    }
    render() {
      return (
        <div className="tabcontent">
          {this.props.content}
        </div>
      );
    }
  }

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

Upvotes: 0

Views: 7922

Answers (2)

Ravindra Ranwala
Ravindra Ranwala

Reputation: 21124

There were lots of issues in your app. I fixed all of them and the new App component should be like this. Firstly inside the render method you need to call the two child components render methods using the (). Next to the renderContent function you need to pass one content instance at a time, not an array. I have posted the App component with all the changes I made to make it work below.

import React, { Component } from 'react';
import Tab from './tab';
import Content from './content';

export default class App extends Component {
  constructor(props) {
        super(props);
        this.state = {
          tabs: [
            {id: 0, name: 'Foo'},
            {id: 1, name: 'Bar'},
            {id: 2, name: 'Fizz'}
          ],
          contents: [
            {name: 'Foo', content: 'Hello'},
            {name: 'Bar', content: 'World'},
            {name: 'Fizz', content: 'Buzz'},
          ]
        };
        this.handleClick = this.handleClick.bind(this);
      }

      handleClick() {
        console.log('Hi');
      }

      renderTab(tab) {
        return(<Tab {...tab}
                    onClick={this.handleClick}
                    />
        );
      }
      renderContent(content) {
        console.log(this.state.contents);
         return(<Content {...content}/> );
      }

      render() {
        return(
          <div className="container-tabs">
            <div className="tab">
              {this.state.tabs.map(tab => this.renderTab(tab))}
            </div>
            <div className="tabcontent">
              <p>{this.state.contents.map(content => this.renderContent(content))}</p>
            </div>
          </div>
        ) ;
      }
}

Finally calling the rendertab function should be as follows. You haven't used map array helper properly.

{this.state.tabs.map(tab => this.renderTab(tab))}

Hope this helps. Happy Coding.

Upvotes: 1

dgrijuela
dgrijuela

Reputation: 763

Add this to App's constructor this.renderTab = this.renderTab.bind(this);

You are losing App's this when doing the map in the render method, that's why it says it's not a function.

Upvotes: 1

Related Questions