0

I am having trouble with passing a function to child in React. I read multiple threads on stackoverflow that talk about binding such functions to this or using arrow functions, but still can not resolve it. Basically I need to pass function called datum to d3.select().datum():

class BarChart extends React.Component {
  constructor(props){
    super(props)
    this.createBarChart = this.createBarChart.bind(this)
  }

  componentDidMount() {
     this.createBarChart()
  }

  componentDidUpdate() {
     this.createBarChart()
  }

  createBarChart() {
    console.log("In createBarChart: " + this.props.datum);
    const node = this.node
    nv.addGraph(function() {
      var chart = nv.models.discreteBarChart()
        .x(function(d) { return d.label })
        .y(function(d) { return d.value })
        .staggerLabels(true)
        //.staggerLabels(historicalBarChart[0].values.length > 8)
        .showValues(true)
        .duration(250)
        ;
    d3.select(node)
        .datum(this.props.datum)
        .call(chart);
    nv.utils.windowResize(chart.update);
    return chart;
});
  }

  render() {
    return <svg ref={node => this.node = node}
      width={1000} height={500}>
    </svg>
  }

}

module.exports = BarChart; 

In the code above d3.select(node) .datum(this.props.datum) .call(chart); causes

TypeError: this.props is undefined

I am trying to pass datum function to the BarChart component in the following way:

import datum from './datum'

class App extends React.Component {
  render() {
    return (
      <DefaultLayout title={this.props.title}>
        <div>Hello {this.props.name}</div>
        <div className='App'>
          <BarChart datum = { datum.bind(this) }/>
        </div>
      </DefaultLayout>
    );
  }
}

module.exports = App;

I have tried to do <BarChart datum = { () => this.datum() }/> but no luck. Then also binding datum function in the constructor of the BarChart component similarly to createBarChart function:

 constructor(props){
     super(props)
     this.createBarChart = this.createBarChart.bind(this)
     this.props.datum = this.props.datum.bind(this)
 } 

The datum function that I am importing as a module from datum.js looks like that:

var datum = function datumFunc() {
   return  [
    {
      key: "Cumulative Return",
      values: [
      ...
      ]
    }
  ]
}

export default datum

Any suggestion would be greatly appreciated.

Nikita Vlasenko
  • 4,004
  • 7
  • 47
  • 87

1 Answers1

1

The anonymous function that you are passing to nv.addGraph is not bound, so this is out of scope when that function is called.

nv.addGraph(function() {
  var chart = nv.models.discreteBarChart()
    .x(function(d) { return d.label })
    .y(function(d) { return d.value })
    .staggerLabels(true)
    //.staggerLabels(historicalBarChart[0].values.length > 8)
    .showValues(true)
    .duration(250)
    ;
  d3.select(node)
    .datum(this.props.datum)
    .call(chart);
  nv.utils.windowResize(chart.update);
  return chart;
}.bind(this));
//^^^^^^^^^^ would fix it

Alternatively you could give that function a name and bind it in the constructor, as you are already doing with createBarChart.

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
  • even in the most recent versions of ReactJS where the constructor has become optional? – HoCo_ Oct 09 '18 at 08:52
  • @HoCo_ I'm not sure which update you refer to specifically, but in any situation where `this` is used in an async context (such the callback here), the appropriate value should be bound. The only modernisation I would consider here would be to use `() => { }` for the function, which is syntactic sugar to achieve the same thing as `function () { }.bind(this)`. – Tom Fenech Oct 09 '18 at 10:48
  • I see, thanks, I talked about the update in which we can now declare the state directly in the pre-render's space in the component, without declaring a constructor, I had some bug this morning to pass a function without declaring a constructor, finally, I have put the function directly in the child as a workaround – HoCo_ Oct 09 '18 at 11:00
  • 1
    @HoCo_ Declaring the state outside of the constructor (in the case where it does not depend on the props) is not related to the need to bind `this` to callback methods. – Tom Fenech Oct 09 '18 at 11:48