Olical / react-faux-dom

DOM like structure that renders to React (unmaintained, archived)
http://oli.me.uk/2015/09/09/d3-within-react-the-right-way/
The Unlicense
1.21k stars 87 forks source link

Animating chart 'this.setState is not a function' #87

Closed Fallenstedt closed 7 years ago

Fallenstedt commented 7 years ago

I'm attempting to animate a bar chart. The chart does animate in correctly, however, I get an error when I call this.animateFauxDom([val]).

The error is

Uncaught TypeError: this.setState is not a function
    at drawFauxDOM (core.js:20)
    at index.js:131
    at index.js:55
drawFauxDOM @ core.js:20
(anonymous) @ index.js:131
(anonymous) @ index.js:55

The line of code at core.js:20:

drawFauxDOM: function () {
    var virtualDOM = mapValues(this.connectedFauxDOM, function (n) {
      return n.toReact()
    })
    this.setState(virtualDOM)
  }

The code that runs this

import React, { Component } from 'react';
import ReactFauxDOM from 'react-faux-dom';//https://github.com/Olical/react-faux-dom
import * as d3 from "d3";
import ReactMixin from 'react-mixin';
import './bar.css';

export default class Bar extends Component {
    constructor(props) {
        super(props)

        this.state = {
            chart: null,
        }
    }

    componentDidMount() {
        let faux = this.connectFauxDOM('div', 'chart')
        let component = this;//smelly....

        var initStackedBarChart = {
            draw: function(config) {
                let me = this;
                let domEle = config.element;
                let stackKey = config.key;
                let data = config.data;
                let margin = {top: 20, right: 20, bottom: 30, left: 80};
                let parseDate = d3.timeParse("%d/%m/%Y");
                let width = 390 - margin.left - margin.right;
                let height = 500 - margin.top - margin.bottom;
                let xScale = d3.scaleLinear().rangeRound([0, width]);
                let yScale = d3.scaleBand().rangeRound([height, 0]).padding(0.1);
                let color = d3.scaleOrdinal(d3.schemeCategory20);

                let svg = d3.select(faux).append("svg")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

                let xAxis = d3.axisBottom(xScale).ticks(5)
                let yAxis =  d3.axisLeft(yScale).tickFormat(d3.timeFormat("%a"));

                var stack = d3.stack()
                .keys(stackKey)
                .offset(d3.stackOffsetNone);

                var layers= stack(data);
                yScale.domain(data.map(function(d) { return parseDate(d.fullDate); }));
                xScale.domain([0, d3.max(layers[layers.length - 1], function(d) {

                    return d[0] +  (d[1]/2) + 10; }) ]).nice();

                    var layer = svg.selectAll(".layer")
                    .data(layers)
                    .enter().append("g")
                    .attr("class", "layer")
                    .style("fill", function(d, i) { return color(i); });
// The rectangles that I want to animate
                    layer.selectAll("rect")
                    .data(function(d) { return d; })
                    .enter().append("rect")
                    .attr("y", function(d) { return yScale(parseDate(d.data.fullDate)); })
                    .attr("x", function(d) { return 0 })
                    .attr("height", yScale.bandwidth())
                    .attr("width", function(d) { return 0 })
                    .transition()
                    .duration(500)
                    .delay(function(d,i) { return i * 100 })
                    .attr("x", function(d) { return xScale(d[0]) })
                    .attr("width", function (d) {
                        return xScale(d[1]) - xScale(d[0]) })

                        //animate bars being drawn in
                    component.animateFauxDOM(800)

                    //x axis
                        svg.append("g")
                        .attr("class", "axis axis-x")
                        .attr("transform", "translate(0," + (height+5) + ")")
                        .call(xAxis);
                    //y axis
                        svg.append("g")
                        .attr("class", "axis axis-y")
                        .attr("transform", "translate(0,0)")
                        .call(yAxis)
                    }
                }

                //draw the chart
                var data = this.props.data;
                var key = ["returningUsers", "newUsers",];
                initStackedBarChart.draw({
                    data: data,
                    key: key,
                    element: 'stacked-bar'
                });
            }

            render() {
                return (
                    <div>
                        {this.state.chart}
                    </div>
                )
            }
        }
        ReactMixin(Bar.prototype, ReactFauxDOM.mixins.core)
        ReactMixin(Bar.prototype, ReactFauxDOM.mixins.anim)

Not sure what is causing it. But if you need me to upload an example for you to play with, let me know and I'll happily do it.

tibotiber commented 7 years ago

Hi @Fallenstedt, the current implementation for animations is using Mixins which I believe is not compatible with React components using ES6 classes. I would advise you to switch to React.createClass instead. There are some examples using this.

Fallenstedt commented 7 years ago

@tibotiber just converted one of my es6 classes to the syntax in your example, and that was it. Thank you for the heads up.

tibotiber commented 7 years ago

@Fallenstedt version 4.0.0 was just published and now support ES6 classes ;)

Fallenstedt commented 7 years ago

💪😎