0

I am new to reactjs and I am unsure how to push a click state back to the parent shell. Some of the components I am building are d3.js charts - and I want to facilitate showing the data - but then having the user choose a node so they could do a drill down on the data.

So if a person clicks on the segment of a pie chart - to get the onclick section and push back the id/data of that node back to parent.

//chart demo http://jsfiddle.net/Qh9X5/10980/

So here is the demo

//fake pie chart

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import * as d3 from "d3";

class DoughPieChart extends Component {
    componentDidMount() {
        var $this = $(ReactDOM.findDOMNode(this));


        var svg = d3.select($this[0])
            .append("svg")
            .append("g")

        svg.append("g")
            .attr("class", "slices");
        svg.append("g")
            .attr("class", "labels");
        svg.append("g")
            .attr("class", "lines");

        var width = 560,
            height = 450,
            radius = Math.min(width, height) / 2;

        var pie = d3.layout.pie()
            .sort(null)
            .value(function(d) {
                return d.value;
            });

        var arc = d3.svg.arc()
            .outerRadius(radius * 0.85)
            .innerRadius(radius * 0.83);

        var outerArc = d3.svg.arc()
            .innerRadius(radius * 0.9)
            .outerRadius(radius * 0.9);

        svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

        var key = function(d){ return d.data.label; };

        var color = d3.scale.ordinal()
            .domain(["Lorem ipsum", "dolor sit", "amet", "consectetur", "adipisicing"])
            .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56"]);

        function randomData (){
            var labels = color.domain();
            return labels.map(function(label){
                return { label: label, value: Math.random() }
            });
        }


        console.log("randomData()", randomData());
        change(randomData());

        d3.select(".randomize")
            .on("click", function(){
                change(randomData());
            });

          function click(d){
          console.log("d", d);
          }


        function change(data) {

            /* ------- PIE SLICES -------*/
            var slice = svg.select(".slices").selectAll("path.slice")
                .data(pie(data), key);

            slice.enter()
                .insert("path")
                .style("fill", function(d) { return color(d.data.label); })
                .attr("class", "slice");

            slice       
                .transition().duration(1000)
                .attrTween("d", function(d) {
                    this._current = this._current || d;
                    var interpolate = d3.interpolate(this._current, d);
                    this._current = interpolate(0);
                    return function(t) {
                        return arc(interpolate(t));
                    };
                })

            slice.exit()
                .remove();

           slice.on("click", function(d){
            click(d)
           })  

        };

    }

    render() {
        return (
            <div 
                className="doughpiechart"
                data-role="doughpiechart" 
                data-width={this.props.width} 
                data-height={this.props.height} 
                data-data={this.props.data}>
            </div>
        );
    }
};

export default DoughPieChart;

//parent shell

import React, { Component } from 'react'
import { NavLink, withRouter, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchDrilldown } from '../../actions/drilldownAction';

// components
import DoughPieChart from './DoughPieChart'

class Parent extends Component {

  constructor(props, context) {
    super(props, context);
    this.submit = this.submit.bind(this);
  }

  submit(data) {
    this.props.fetchDrilldown(data);
  }

  render() {
    return (
      <div>
        <DoughPieChart onClickSegment={this.submit} />
      </div>
    )
  }

}

function mapStateToProps(state) {
  return {
    drilldownData: state.drilldown
  };
}

function mapDispatchToProps(dispatch) {
 return bindActionCreators({ fetchDrilldown }, dispatch);
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Parent))
The Old County
  • 89
  • 13
  • 59
  • 129

1 Answers1

1

You can always communicate between child and parent components easily in React. To keep the state consistent, you can follow the steps to achieve clicking the button from the child component and update the state / data in parent to render it:

  1. Create a state variable to hold the state of parent in the parent component. For example, id or data
  2. Create a function to update the parent state and pass it down to the child components
  3. Call the function above to update the parent state variable you created in step 1

So the example code should look like this: In the parent component:

  constructor(props, context) {
    super(props, context);
    this.submit = this.submit.bind(this);
    this.state.data = null;
    this.setParentState = this.setParentState.bind(this);
  }

  setParentState(obj) {
    Object.keys(obj).forEach((key) => {
      this.setState({
        [key]: obj[key]
      });
    });
  }

  render() {
    return (
      <div>
        <DoughPieChart setParentState={this.setParentState} />
      </div>
    )
  }

And in your child component you can call

  this.props.setParentState({
     data: THE_DATA_YOU_WANT_TO_UPDATE
  })

to update the parent state and display it in your parent component.

Hope it makes sense to you.

MattYao
  • 2,495
  • 15
  • 21
  • In your example - it would just become "" -- so in the DoughPieChart component itself -- just append this.props.setParentState with the data and it will become accessible to the parent shell. – The Old County Oct 04 '17 at 05:33
  • "onClickSegment={this.submit}" - is redundant – The Old County Oct 04 '17 at 05:34
  • Yes, that's correct. The point is to give the access to the child component and set its parent state. I am glad you get through it. – MattYao Oct 04 '17 at 07:55
  • Cheers @MattYao - I have an extension to this problem in this thread now - https://stackoverflow.com/questions/46562233/reactjs-redirecting-from-a-child-event – The Old County Oct 04 '17 at 10:11