Nooobi3
Nooobi3

Reputation: 43

Why am I getting an uncaught TypeError when I pass a function to a child component in React.js?

This is a small chunk of a larger codebase. But I believe this is the only relevant part needed for this question.

I have two components DataSeries and Sector. The DataSeries component is rendered by another parent component. And the Sector component is rendered by the DataSeries component. I'm passing a function as a property from DataSeries to Sector. But when I try to call that function from Sector I'm getting an Uncaught TypeError: this.props.someFunc is not a function error. And I'm not really sure why. Any help?

Here is the code.

var Sector = React.createClass({
  getInitialState: function() {
    return {text: '', count:'', opacity:'arc'};
  },
  formatNumber: function(num, digits) {
      //irrelevant function
      return num;
  },
  render: function() {
    var outerRadius = this.props.width/2.2;
    var innerRadius = this.props.width/3.5;
    var arc = d3.svg.arc()
        .outerRadius(outerRadius)
        .innerRadius(innerRadius);
    var data = this.props.data;
    var center = "translate(" + arc.centroid(data) + ")";
    var nameCenter = "translate(0,0)";
    var countCenter = "translate(0,50)"
    var color = this.props.colors;
    var formattedCount = this.formatNumber(this.state.count, 1)
    return (
      <g onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut} onClick={this.onClick}>
        <path className={this.state.opacity} fill={color[this.props.ikey]} d={arc(this.props.data)}></path>
        <text fill={color[this.props.ikey]} stroke={color} fontSize="27px" transform={nameCenter} textAnchor="middle">{this.state.text}</text>
        <text fill={"#868686"} stroke={color} fontSize="25px" transform={countCenter} textAnchor="middle">{formattedCount}</text>
      </g>
    );
  },

  onMouseOver: function() {

    this.setState({text: '', count:'', opacity:'arc-hover'});
    this.setState({text: this.props.name, count:this.props.data.value});
    console.log("Inside onMouseOver")
    this.props.someFunc();
  },
  onMouseOut: function() {
    this.setState({text: '', count: '', opacity:'arc'});
  },
  onClick: function() {
    //Do something on click?
  }
});

var DataSeries = React.createClass({
  propTypes: {
    width: React.PropTypes.number.isRequired,
    height: React.PropTypes.number.isRequired,
    color: React.PropTypes.array,
    data: React.PropTypes.array.isRequired,
  },
  randomFunction: function() {
    console.log("Random Function")
  },
  render: function() {
    var color = this.props.colors;
    var data = this.props.data;
    var width = this.props.width;
    var height = this.props.height;
    var pie = d3.layout.pie();
    var result = data.map(function(item){
      return item.count;
    })
    console.log(result)
    var names = data.map(function(item){
      return item.name;
    })
    var sum = result.reduce(function(memo, num){ return memo + num; }, 0);
    var position = "translate(" + (width)/2 + "," + (height)/2 + ")";
    var arcs = (pie(result)).map(function(point, i) {
      return (
        <Sector 
           data={point} 
           ikey={i} 
           key={i} 
           name={names[i]} 
           colors={color} 
           total={sum} 
           width={width} 
           height={height} 
           someFunc={this.randomFunction}/>
      )
    });
    return (
        <g transform={position}>
        {arcs}
        </g>
    );
  }
});

Upvotes: 2

Views: 76

Answers (1)

Matthew Herbst
Matthew Herbst

Reputation: 31973

When you use map, the context represented by this changes. So, currently, inside your map call, this.randomFunction is actually "undefined".

So, to use the Sector's context of this that has your function in it, you need to bind Sector's this to the map's anonymous function.

var arcs = (pie(result)).map(function(point, i) {
  return (
    <Sector 
      data={point} 
      ikey={i} 
      key={i} 
      name={names[i]} 
      colors={color} 
      total={sum} 
      width={width} 
      height={height} 
      someFunc={this.randomFunction}
    />
  )
}.bind(this));

What this is doing is setting the context of this inside of the anonymous function to the this from Sector.

Upvotes: 3

Related Questions