2

I have some trouble understanding how to pass component instance variables in React (live example). My hierarchy is as follows:

DataView
 |> SharedScale
 |> Graph1
 |> Graph2
 |> ...

The idea is to create a shared, D3 axis/scale in SharedScale and pass it to all other graphs via the DataView wrapper (i.e. a reference scale is supposed to be shown at the top of the page). The scale implementation can change, when the component is resized for example (i.e. it's created in the render function in my case):

class SharedScale extends React.Component {    
  render() {  
    // Create a D3 scale; i.e. an external library object, which is not serializable
    this.scaleImpl = ...

    // Render a D3 axis using the scale implementation
    return <div>{this.scaleImpl}</div>; 
  }
};

The Graph is supposed to get a reference to SharedScale, and render a D3 chart, based on it:

class Graph extends React.Component {
   render() {    
     //Use the scaleImpl here to render the graph body
     return (
       <div>{get(this.props, "scaleComponent.scaleImpl", "no scale implementation")}</div>
     );
   }
}

I'm trying to combine this together in the DataView, using React.createRef as such:

class DataView extends React.Component {   
    constructor(props) {
      super(props);

      this.scaleComponent = React.createRef();
    }

    render() {
        return(
          <div>
            <SharedScale ref={this.scaleComponent} />
            <Graph scaleComponent={this.scaleComponent.current} />                
          </div>
        );
     }
}

The problem is that the render() function is only called once. That is, the current field is consistently null.

Am I missing something, or is this approach fundamentally flawed for some reason?

Szymon Jednac
  • 2,969
  • 1
  • 29
  • 43

1 Answers1

0

As it turns out, one can trigger an additional render event by calling forceUpdate in componentDidMount:

class DataView extends React.Component {   
  ...

  componentDidMount() {
    this.forceUpdate();
  }
}

From componentDidMount reference page:

You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state. Use this pattern with caution because it often causes performance issues. In most cases, you should be able to assign the initial state in the constructor() instead. It can, however, be necessary for cases like modals and tooltips when you need to measure a DOM node before rendering something that depends on its size or position.

This is more or less equivalent to calling forceUpdate.

Updated live example can be found here.

Szymon Jednac
  • 2,969
  • 1
  • 29
  • 43