From 16e9e26157d9d3a9f22f88d9d81fa339214a3179 Mon Sep 17 00:00:00 2001 From: Josh Baker Date: Tue, 5 Jul 2016 17:35:46 -0700 Subject: [PATCH] Improvements to component updating --- dist/react-chartjs-2.js | 110 ++++++++++++++++++++++++++++++++---- dist/react-chartjs-2.min.js | 2 +- lib/Chart.js | 54 +++++++++++++++--- lib/utils/deepEqual.js | 52 +++++++++++++++++ package.json | 4 +- src/Chart.js | 95 ++++++++++++++++++++----------- src/utils/deepEqual.js | 47 +++++++++++++++ 7 files changed, 310 insertions(+), 54 deletions(-) create mode 100644 lib/utils/deepEqual.js create mode 100644 src/utils/deepEqual.js diff --git a/dist/react-chartjs-2.js b/dist/react-chartjs-2.js index b4200183f..ea4bcf489 100644 --- a/dist/react-chartjs-2.js +++ b/dist/react-chartjs-2.js @@ -29,6 +29,10 @@ var _chartJs = require('chart.js'); var _chartJs2 = _interopRequireDefault(_chartJs); +var _utilsDeepEqual = require('./utils/deepEqual'); + +var _utilsDeepEqual2 = _interopRequireDefault(_utilsDeepEqual); + var ChartComponent = _react2['default'].createClass({ displayName: 'ChartComponent', @@ -52,7 +56,7 @@ var ChartComponent = _react2['default'].createClass({ type: 'doughnut', height: 200, width: 200, - redraw: true + redraw: false }; }, @@ -68,23 +72,55 @@ var ChartComponent = _react2['default'].createClass({ if (this.props.redraw) { this.chart_instance.destroy(); this.renderChart(); + } else { + this.updateChart(); + } + }, + + _objectWithoutProperties: function _objectWithoutProperties(obj, keys) { + var target = {}; + for (var i in obj) { + if (keys.indexOf(i) >= 0) continue; + if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; + target[i] = obj[i]; } + return target; }, - shouldComponentUpdate: function shouldComponentUpdate(nextProps) { - return this.props !== nextProps; + shouldComponentUpdate: function shouldComponentUpdate(nextProps, nextState) { + var compareNext = this._objectWithoutProperties(nextProps, ["id", "width", "height"]); + var compareNow = this._objectWithoutProperties(this.props, ["id", "width", "height"]); + return !(0, _utilsDeepEqual2['default'])(compareNext, compareNow, { strict: true }); }, componentWillUnmount: function componentWillUnmount() { this.chart_instance.destroy(); }, - renderChart: function renderChart() { + updateChart: function updateChart() { + var _this = this; + var _props = this.props; var data = _props.data; var options = _props.options; - var legend = _props.legend; - var type = _props.type; + + this.chart_instance.options.scales.xAxes[0].ticks.min = options.scales.xAxes[0].ticks.min; + this.chart_instance.options.scales.xAxes[0].ticks.max = options.scales.xAxes[0].ticks.max; + this.chart_instance.options.scales.xAxes[0].fixedBarWidth = options.scales.xAxes[0].fixedBarWidth; + + data.datasets.forEach(function (dataset, index) { + _this.chart_instance.data.datasets[index].backgroundColor = dataset.backgroundColor; + }); + + this.chart_instance.update(); + }, + + renderChart: function renderChart() { + var _props2 = this.props; + var data = _props2.data; + var options = _props2.options; + var legend = _props2.legend; + var type = _props2.type; var node = _reactDom2['default'].findDOMNode(this); @@ -96,9 +132,9 @@ var ChartComponent = _react2['default'].createClass({ }, render: function render() { - var _props2 = this.props; - var height = _props2.height; - var width = _props2.width; + var _props3 = this.props; + var height = _props3.height; + var width = _props3.width; return _react2['default'].createElement('canvas', { height: height, @@ -134,5 +170,59 @@ function Polar(props) { } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"chart.js":undefined,"react-dom":undefined}]},{},[1])(1) +},{"./utils/deepEqual":2,"chart.js":undefined,"react-dom":undefined}],2:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +var hasOwnProperty = Object.prototype.hasOwnProperty; + +var is = function is(x, y) { + // SameValue algorithm + if (x === y) { + // Steps 1-5, 7-10 + // Steps 6.b-6.e: +0 != -0 + return x !== 0 || 1 / x === 1 / y; + } else { + // Step 6.a: NaN == NaN + return x !== x && y !== y; + } +}; + +var deepEqual = function deepEqual(objA, objB) { + if (is(objA, objB)) { + return true; + } + + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + + var keysA = Object.keys(objA); + + // Test for A's keys different from B. + for (var i = 0; i < keysA.length; i++) { + if (!hasOwnProperty.call(objB, keysA[i])) { + return false; + } + } + + for (var propty in objA) { + if (objB.hasOwnProperty(propty)) { + if (!deepEqual(objA[propty], objB[propty])) { + return false; + } + } else { + return false; + } + } + + return true; +}; + +exports['default'] = deepEqual; +module.exports = exports['default']; + +},{}]},{},[1])(1) }); \ No newline at end of file diff --git a/dist/react-chartjs-2.min.js b/dist/react-chartjs-2.min.js index 0f52195f5..ea5a54408 100644 --- a/dist/react-chartjs-2.min.js +++ b/dist/react-chartjs-2.min.js @@ -1 +1 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.Chart=e()}}(function(){return function e(t,n,r){function o(a,u){if(!n[a]){if(!t[a]){var d="function"==typeof require&&require;if(!u&&d)return d(a,!0);if(i)return i(a,!0);var f=new Error("Cannot find module '"+a+"'");throw f.code="MODULE_NOT_FOUND",f}var p=n[a]={exports:{}};t[a][0].call(p.exports,function(e){var n=t[a][1][e];return o(n?n:e)},p,p.exports,e,t,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;a=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n},shouldComponentUpdate:function(e,t){var n=this._objectWithoutProperties(e,["id","width","height"]),r=this._objectWithoutProperties(this.props,["id","width","height"]);return!(0,w["default"])(n,r,{strict:!0})},componentWillUnmount:function(){this.chart_instance.destroy()},updateChart:function(){var e=this,t=this.props,n=t.data,r=t.options;this.chart_instance.options.scales.xAxes[0].ticks.min=r.scales.xAxes[0].ticks.min,this.chart_instance.options.scales.xAxes[0].ticks.max=r.scales.xAxes[0].ticks.max,this.chart_instance.options.scales.xAxes[0].fixedBarWidth=r.scales.xAxes[0].fixedBarWidth,n.datasets.forEach(function(t,n){e.chart_instance.data.datasets[n].backgroundColor=t.backgroundColor}),this.chart_instance.update()},renderChart:function(){var e=this.props,t=e.data,n=e.options,r=(e.legend,e.type),o=h["default"].findDOMNode(this);this.chart_instance=new b["default"](o,{type:r,data:t,options:n})},render:function(){var e=this.props,t=e.height,n=e.width;return p["default"].createElement("canvas",{height:t,width:n})}});n["default"]=v}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./utils/deepEqual":2,"chart.js":void 0,"react-dom":void 0}],2:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var r=Object.prototype.hasOwnProperty,o=function(e,t){return e===t?0!==e||1/e===1/t:e!==e&&t!==t},i=function a(e,t){if(o(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;for(var n=Object.keys(e),i=0;i= 0) continue; + if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; + target[i] = obj[i]; } + return target; }, - shouldComponentUpdate: function shouldComponentUpdate(nextProps) { - return this.props !== nextProps; + shouldComponentUpdate: function shouldComponentUpdate(nextProps, nextState) { + var compareNext = this._objectWithoutProperties(nextProps, ["id", "width", "height"]); + var compareNow = this._objectWithoutProperties(this.props, ["id", "width", "height"]); + return !(0, _utilsDeepEqual2['default'])(compareNext, compareNow, { strict: true }); }, componentWillUnmount: function componentWillUnmount() { this.chart_instance.destroy(); }, - renderChart: function renderChart() { + updateChart: function updateChart() { + var _this = this; + var _props = this.props; var data = _props.data; var options = _props.options; - var legend = _props.legend; - var type = _props.type; + + this.chart_instance.options.scales.xAxes[0].ticks.min = options.scales.xAxes[0].ticks.min; + this.chart_instance.options.scales.xAxes[0].ticks.max = options.scales.xAxes[0].ticks.max; + this.chart_instance.options.scales.xAxes[0].fixedBarWidth = options.scales.xAxes[0].fixedBarWidth; + + data.datasets.forEach(function (dataset, index) { + _this.chart_instance.data.datasets[index].backgroundColor = dataset.backgroundColor; + }); + + this.chart_instance.update(); + }, + + renderChart: function renderChart() { + var _props2 = this.props; + var data = _props2.data; + var options = _props2.options; + var legend = _props2.legend; + var type = _props2.type; var node = _reactDom2['default'].findDOMNode(this); @@ -94,9 +130,9 @@ var ChartComponent = _react2['default'].createClass({ }, render: function render() { - var _props2 = this.props; - var height = _props2.height; - var width = _props2.width; + var _props3 = this.props; + var height = _props3.height; + var width = _props3.width; return _react2['default'].createElement('canvas', { height: height, diff --git a/lib/utils/deepEqual.js b/lib/utils/deepEqual.js new file mode 100644 index 000000000..91c284984 --- /dev/null +++ b/lib/utils/deepEqual.js @@ -0,0 +1,52 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +var hasOwnProperty = Object.prototype.hasOwnProperty; + +var is = function is(x, y) { + // SameValue algorithm + if (x === y) { + // Steps 1-5, 7-10 + // Steps 6.b-6.e: +0 != -0 + return x !== 0 || 1 / x === 1 / y; + } else { + // Step 6.a: NaN == NaN + return x !== x && y !== y; + } +}; + +var deepEqual = function deepEqual(objA, objB) { + if (is(objA, objB)) { + return true; + } + + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + + var keysA = Object.keys(objA); + + // Test for A's keys different from B. + for (var i = 0; i < keysA.length; i++) { + if (!hasOwnProperty.call(objB, keysA[i])) { + return false; + } + } + + for (var propty in objA) { + if (objB.hasOwnProperty(propty)) { + if (!deepEqual(objA[propty], objB[propty])) { + return false; + } + } else { + return false; + } + } + + return true; +}; + +exports['default'] = deepEqual; +module.exports = exports['default']; \ No newline at end of file diff --git a/package.json b/package.json index 39c6bfd6b..7170589ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-chartjs-2", - "version": "1.1.5", + "version": "1.1.6", "description": "react-chartjs-2", "main": "lib/Chart.js", "author": "Goran Udosic", @@ -13,7 +13,7 @@ "url": "https://github.com/gor181/react-chartjs-2/issues" }, "dependencies": { - "chart.js": "^2.1.0" + "chart.js": "^2.2.0-rc.1" }, "devDependencies": { "babel-eslint": "^4.1.3", diff --git a/src/Chart.js b/src/Chart.js index 1b59b2546..5803bfd53 100644 --- a/src/Chart.js +++ b/src/Chart.js @@ -1,6 +1,7 @@ import React, {PropTypes} from 'react'; import ReactDOM from 'react-dom'; import Chart from 'chart.js'; +import deepEqual from './utils/deepEqual'; const ChartComponent = React.createClass({ @@ -22,10 +23,10 @@ const ChartComponent = React.createClass({ display: true, position: 'bottom' }, - type: 'doughnut', + type: 'doughnut', height: 200, width: 200, - redraw: true + redraw: false }; }, @@ -34,69 +35,99 @@ const ChartComponent = React.createClass({ }, componentDidMount() { - this.renderChart(); - }, + this.renderChart(); + }, - componentDidUpdate() { + componentDidUpdate() { if (this.props.redraw) { this.chart_instance.destroy(); - this.renderChart(); + this.renderChart(); + } else { + this.updateChart(); } - }, + }, - shouldComponentUpdate(nextProps) { - return this.props !== nextProps; + _objectWithoutProperties (obj, keys) { + var target = {}; + for (var i in obj) { + if (keys.indexOf(i) >= 0) continue; + if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; + target[i] = obj[i]; + } + return target; }, - componentWillUnmount() { - this.chart_instance.destroy(); - }, + shouldComponentUpdate(nextProps, nextState) { + const compareNext = this._objectWithoutProperties(nextProps, ["id", "width", "height"]); + const compareNow = this._objectWithoutProperties(this.props, ["id", "width", "height"]); + return !deepEqual(compareNext, compareNow, {strict: true}); + }, - renderChart() { - const {data, options, legend, type} = this.props; - const node = ReactDOM.findDOMNode(this); + componentWillUnmount() { + this.chart_instance.destroy(); + }, + + updateChart() { + const {data, options} = this.props; + + if (!this.chart_instance) return; + + this.chart_instance.options.scales.xAxes[0].ticks.min = options.scales.xAxes[0].ticks.min; + this.chart_instance.options.scales.xAxes[0].ticks.max = options.scales.xAxes[0].ticks.max; + this.chart_instance.options.scales.xAxes[0].fixedBarWidth = options.scales.xAxes[0].fixedBarWidth; + + data.datasets.forEach((dataset, index) => { + this.chart_instance.data.datasets[index].backgroundColor = dataset.backgroundColor; + }); + + this.chart_instance.update(); + }, - this.chart_instance = new Chart(node, { - type, - data, - options - }); - }, + renderChart() { + const {data, options, legend, type} = this.props; + const node = ReactDOM.findDOMNode(this); + + this.chart_instance = new Chart(node, { + type, + data, + options + }); + }, - render() { + render() { const {height, width} = this.props; - return ( - - ); - } + ); + } }); export default ChartComponent; -export function Doughnut(props) { +export function Doughnut (props) { return ; } -export function Pie(props) { +export function Pie (props) { return ; } -export function Line(props) { +export function Line (props) { return ; } -export function Bar(props) { +export function Bar (props) { return ; } -export function Radar(props) { +export function Radar (props) { return ; } -export function Polar(props) { +export function Polar (props) { return ; } diff --git a/src/utils/deepEqual.js b/src/utils/deepEqual.js new file mode 100644 index 000000000..53410e66f --- /dev/null +++ b/src/utils/deepEqual.js @@ -0,0 +1,47 @@ +let hasOwnProperty = Object.prototype.hasOwnProperty; + +const is = (x, y) => { + // SameValue algorithm + if (x === y) { + // Steps 1-5, 7-10 + // Steps 6.b-6.e: +0 != -0 + return x !== 0 || 1 / x === 1 / y; + } else { + // Step 6.a: NaN == NaN + return x !== x && y !== y; + } +}; + +const deepEqual = (objA, objB) => { + if (is(objA, objB)) { + return true; + } + + if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { + return false; + } + + let keysA = Object.keys(objA); + + // Test for A's keys different from B. + for (let i = 0; i < keysA.length; i++) { + if (!hasOwnProperty.call(objB, keysA[i])) { + return false; + } + } + + for (let propty in objA) { + if (objB.hasOwnProperty(propty)) { + if (!deepEqual(objA[propty], objB[propty])) { + return false; + } + } + else { + return false; + } + } + + return true; +}; + +export default deepEqual;