1

This component gets created as part of a page view that users navigate to off of the initial dashboard. All of the data that the svg needs to render correctly appears to be logging correctly but the SVG just isn't being created on initial navigation to this view. The function is running but no SVG is showing up in the html. At first I thought it was because the image hadn't been loaded yet, but the SVG is being appended to the div not directly to the image so it would at the very least I think show up in the html minus width and height. Any help would be much appreciated.

import React, { Component, PropTypes } from 'react';
import $ from 'jquery';
import { connect } from 'react-redux';

class BrainComponent extends Component {
  static propTypes = {
  showBrainData: PropTypes.bool.isRequired,
  showThisHitData: PropTypes.object,
  hit: PropTypes.object
};

constructor(props, context) {
    super(props, context);
    this.state = {
        showBrainData: this.props.showBrainData,
    };
}

componentWillMount() {
    console.log(this.props);
}

handleBrainVizColor() {
    console.log(this.props.hit.Hic);
    let colorString;
    const hic = this.props.hit.Hic;
    const redNum = 80 + (Math.round(hic / 2));
    const greeNum = 180 - (Math.round(hic / 2));
    colorString = "rgba(" + redNum + "," + greeNum + ", 0, .4)";
    return colorString;
}

renderBrainViz() {
    console.log(this.props.hit);
    this.renderRecangles();
}

componentDidMount() {
    // this.renderBrainViz.bind(this);
}

renderRecangles() {
    d3.select(".brain-canvas").remove();
    const fillColor = this.handleBrainVizColor();
    console.log(this.props.showBrainData);
    if (this.props.showBrainData) {
    const brainWidth = $(".brain-img").width();
    const brainHeight = $(".brain-img").height();
    let xLocation;
    let yLocation;
    let width = brainWidth / 2;
    let height = brainHeight / 2;
    console.log(this.props.hit.ImpactDirection);

    switch (this.props.hit.ImpactDirection) {
        case 'URP':
            xLocation = brainWidth / 2;
            yLocation = 0;
        break;
        case 'LRP':
            xLocation = 0;
            yLocation = brainHeight / 2;
            height += 10;
            break;
        case 'CR':
            break;
        case 'LR':
            xLocation = 0;
            yLocation = 0;
            width = brainWidth;
            break;
        case 'UR':
            xLocation = 0;
            yLocation = brainWidth / 2;
            width = brainWidth;
            break;
        case 'BA':
            break;
        case 'LF':
            xLocation = 0;
            yLocation = 0;
            width = brainWidth;
            break;
        case 'UF':
            xLocation = 0;
            yLocation = 0;
            width = brainWidth;
            break;
        case 'ULP':
            xLocation = 0;
            yLocation = 0;
            break;
        case 'LLP':
            xLocation = 0;
            yLocation = 0;
            break;
        }

    const brainCanvas = d3.select(".brain-one").append("svg")
                .attr("width", brainWidth)
                .attr("height", brainHeight)
                .attr("class", "brain-canvas");
    brainCanvas.append("rect")
            .attr("x", xLocation)
            .attr("y", yLocation)
            .attr("width", width)
            .attr("height", height)
            .style("fill", fillColor);
    }
}

render () {
        return (
          <div className="brain-container">
              <div className="brain-one">
                  <img className="brain-img" src="https://s3-us-west-2.amazonaws.com/mvtrak/new-brain.png" />
              </div>
              { this.renderBrainViz() }
              { !this.props.showBrainData ? <div>
                <div className="brain-overlay"></div>
                <div className="select-hit-text">SELECT HIT BELOW</div>
              </div> : null }
          </div>
    );
 }
}

function mapStateToProps(state) {
   console.log(state);
   return {
      hit: state.hitData
   };
}

export default connect(mapStateToProps)(BrainComponent);

1 Answer 1

4

It's inappropriate to call this.renderBrainViz() from within render(), as you're showing, because the DOM elements created inside render() are still shadow DOM elements at that point. They only become real DOM elements after render() is done and React does its thing to diff and actualize those shadow DOM elements into real DOM. In other words, the first time render() is called, which in turn calls this.renderBrainViz(), this line:

d3.select(".brain-one").append("svg")

has no .brain-one to select yet, because it doesn't exist.

It works on the 2nd call to render() because at that point .brain-one already exists from the first call, but that's a lucky coincidence. I'd still consider it out of sync.

The proper way is to NEVER call this.renderRectangles() from within render() and instead call it from within componentDidUpdate() and also within componentDidMount() as you had but commented out. (Using .bind() is unnecessary there).

Also, although it might be ok to call d3.select(".brain-one") from within renderRectangles(), it would be a problem if there exist multiple BrainComponents, because then there would be multiple .brain-one and d3 would select the first one encountered. A better way is to use React's ref feature to set a reference to .brain-one:

<div className="brain-one" ref="brainOne">

That way, you can do something like

d3.select(this.refs.brainOne).append("svg")

Finally, it's probably not a good idea to, within renderRectangles(), always be removing and recreating .brain-canvas. Instead, you should never remove it and only create it conditionally, if it doesn't exist. You can check if .brain-canvas exists like this:

if(d3.select(this.refs.brainOne).select('.brain-canvas').empty()) {
  // means it doesn't exist
}

Or, you can use a sort of trick that will return an .enter() set of size 1 or 0, depending on whether it already exists, using the following:

d3.select(this.refs.brainOne).selectAll('.brain-canvas')
  .data([null]) // bind to a data array of 1 element
.enter()
  .append('svg') // will only append the svg if it doesn't exist yet
  .attr('class', 'brain-canvas')
  ....
Sign up to request clarification or add additional context in comments.

1 Comment

This is amazingly helpful and all makes a ton of sense.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.