diff --git a/caravel/assets/javascripts/SqlLab/components/AceEditorWrapper.jsx b/caravel/assets/javascripts/SqlLab/components/AceEditorWrapper.jsx
new file mode 100644
index 0000000000000..4f458d8d4d5c1
--- /dev/null
+++ b/caravel/assets/javascripts/SqlLab/components/AceEditorWrapper.jsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import AceEditor from 'react-ace';
+import 'brace/mode/sql';
+import 'brace/theme/github';
+import 'brace/ext/language_tools';
+
+const propTypes = {
+ sql: React.PropTypes.string.isRequired,
+ onBlur: React.PropTypes.func,
+};
+
+const defaultProps = {
+ onBlur: () => {},
+};
+
+class AceEditorWrapper extends React.PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ sql: props.sql,
+ };
+ }
+ textChange(text) {
+ this.setState({ sql: text });
+ }
+ onBlur() {
+ this.props.onBlur(this.state.sql);
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
+AceEditorWrapper.defaultProps = defaultProps;
+AceEditorWrapper.propTypes = propTypes;
+
+export default AceEditorWrapper;
diff --git a/caravel/assets/javascripts/SqlLab/components/Alerts.jsx b/caravel/assets/javascripts/SqlLab/components/Alerts.jsx
index f009fdd2f1ad6..c635b5ae5aab3 100644
--- a/caravel/assets/javascripts/SqlLab/components/Alerts.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/Alerts.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import { Alert } from 'react-bootstrap';
-class Alerts extends React.Component {
+class Alerts extends React.PureComponent {
removeAlert(alert) {
this.props.actions.removeAlert(alert);
}
diff --git a/caravel/assets/javascripts/SqlLab/components/App.jsx b/caravel/assets/javascripts/SqlLab/components/App.jsx
index dd46ef10f0c1b..a43751156d300 100644
--- a/caravel/assets/javascripts/SqlLab/components/App.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/App.jsx
@@ -10,7 +10,7 @@ import DataPreviewModal from './DataPreviewModal';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
-class App extends React.Component {
+class App extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
diff --git a/caravel/assets/javascripts/SqlLab/components/CopyQueryTabUrl.jsx b/caravel/assets/javascripts/SqlLab/components/CopyQueryTabUrl.jsx
index 3b725d5cc122e..b011631406a11 100644
--- a/caravel/assets/javascripts/SqlLab/components/CopyQueryTabUrl.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/CopyQueryTabUrl.jsx
@@ -10,7 +10,7 @@ const defaultProps = {
queryEditor: null,
};
-export default class CopyQueryTabUrl extends React.Component {
+export default class CopyQueryTabUrl extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
diff --git a/caravel/assets/javascripts/SqlLab/components/DataPreviewModal.jsx b/caravel/assets/javascripts/SqlLab/components/DataPreviewModal.jsx
index 40e078d284b7f..cbea27fdf22cd 100644
--- a/caravel/assets/javascripts/SqlLab/components/DataPreviewModal.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/DataPreviewModal.jsx
@@ -13,7 +13,7 @@ const propTypes = {
dataPreviewQueryId: React.PropTypes.string,
};
-class DataPreviewModal extends React.Component {
+class DataPreviewModal extends React.PureComponent {
hide() {
this.props.actions.hideDataPreview();
}
diff --git a/caravel/assets/javascripts/SqlLab/components/DatabaseSelect.jsx b/caravel/assets/javascripts/SqlLab/components/DatabaseSelect.jsx
index 6719db521916e..a187410434730 100644
--- a/caravel/assets/javascripts/SqlLab/components/DatabaseSelect.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/DatabaseSelect.jsx
@@ -2,7 +2,7 @@ const $ = window.$ = require('jquery');
import React from 'react';
import Select from 'react-select';
-class DatabaseSelect extends React.Component {
+class DatabaseSelect extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
diff --git a/caravel/assets/javascripts/SqlLab/components/Link.jsx b/caravel/assets/javascripts/SqlLab/components/Link.jsx
index 2f322dc82b7d4..b4cd16fd32935 100644
--- a/caravel/assets/javascripts/SqlLab/components/Link.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/Link.jsx
@@ -20,7 +20,7 @@ const defaultProps = {
};
-class Link extends React.Component {
+class Link extends React.PureComponent {
render() {
let tooltip = (
diff --git a/caravel/assets/javascripts/SqlLab/components/QueryAutoRefresh.jsx b/caravel/assets/javascripts/SqlLab/components/QueryAutoRefresh.jsx
index 851ab35001a2f..977ec50cd2b42 100644
--- a/caravel/assets/javascripts/SqlLab/components/QueryAutoRefresh.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/QueryAutoRefresh.jsx
@@ -7,7 +7,7 @@ const $ = require('jquery');
const QUERY_UPDATE_FREQ = 1000;
const QUERY_UPDATE_BUFFER_MS = 5000;
-class QueryAutoRefresh extends React.Component {
+class QueryAutoRefresh extends React.PureComponent {
componentWillMount() {
this.startTimer();
}
diff --git a/caravel/assets/javascripts/SqlLab/components/QuerySearch.jsx b/caravel/assets/javascripts/SqlLab/components/QuerySearch.jsx
index a4f963fe500cd..36d834d353f8d 100644
--- a/caravel/assets/javascripts/SqlLab/components/QuerySearch.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/QuerySearch.jsx
@@ -11,7 +11,7 @@ const propTypes = {
actions: React.PropTypes.object.isRequired,
};
-class QuerySearch extends React.Component {
+class QuerySearch extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
diff --git a/caravel/assets/javascripts/SqlLab/components/QueryTable.jsx b/caravel/assets/javascripts/SqlLab/components/QueryTable.jsx
index fc0f050245f8d..763b6dcd0f116 100644
--- a/caravel/assets/javascripts/SqlLab/components/QueryTable.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/QueryTable.jsx
@@ -27,7 +27,7 @@ const defaultProps = {
};
-class QueryTable extends React.Component {
+class QueryTable extends React.PureComponent {
constructor(props) {
super(props);
const uri = window.location.toString();
diff --git a/caravel/assets/javascripts/SqlLab/components/ResultSet.jsx b/caravel/assets/javascripts/SqlLab/components/ResultSet.jsx
index 63ea5f7050a81..a6aade429c926 100644
--- a/caravel/assets/javascripts/SqlLab/components/ResultSet.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/ResultSet.jsx
@@ -25,7 +25,7 @@ const defaultProps = {
};
-class ResultSet extends React.Component {
+class ResultSet extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
diff --git a/caravel/assets/javascripts/SqlLab/components/SouthPane.jsx b/caravel/assets/javascripts/SqlLab/components/SouthPane.jsx
index 506aaee29a8f2..9b0cbd554d17e 100644
--- a/caravel/assets/javascripts/SqlLab/components/SouthPane.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/SouthPane.jsx
@@ -1,6 +1,7 @@
import { Alert, Tab, Tabs } from 'react-bootstrap';
import QueryHistory from './QueryHistory';
import ResultSet from './ResultSet';
+import { areArraysShallowEqual } from '../../reduxUtils';
import React from 'react';
import shortid from 'shortid';
@@ -10,32 +11,40 @@ const propTypes = {
actions: React.PropTypes.object.isRequired,
};
-const SouthPane = function (props) {
- let latestQuery;
- if (props.queries.length > 0) {
- latestQuery = props.queries[props.queries.length - 1];
+class SouthPane extends React.PureComponent {
+ shouldComponentUpdate(nextProps) {
+ return !areArraysShallowEqual(this.props.queries, nextProps.queries);
}
- let results;
- if (latestQuery) {
- results = ;
- } else {
- results = Run a query to display results here;
+ render() {
+ let latestQuery;
+ const props = this.props;
+ if (props.queries.length > 0) {
+ latestQuery = props.queries[props.queries.length - 1];
+ }
+ let results;
+ if (latestQuery) {
+ results = (
+
+ );
+ } else {
+ results = Run a query to display results here;
+ }
+ return (
+
+
+
+
+ {results}
+
+
+
+
+
+
+
+ );
}
- return (
-
-
-
-
- {results}
-
-
-
-
-
-
-
- );
-};
+}
SouthPane.propTypes = propTypes;
export default SouthPane;
diff --git a/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx b/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
index af0aaaeb4cd66..82d69a28d814f 100644
--- a/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/SqlEditor.jsx
@@ -13,16 +13,11 @@ import {
Tooltip,
} from 'react-bootstrap';
-import AceEditor from 'react-ace';
-import 'brace/mode/sql';
-import 'brace/theme/github';
-import 'brace/ext/language_tools';
-
-import shortid from 'shortid';
import SouthPane from './SouthPane';
import Timer from './Timer';
import SqlEditorLeftBar from './SqlEditorLeftBar';
+import AceEditorWrapper from './AceEditorWrapper';
const propTypes = {
actions: React.PropTypes.object.isRequired,
@@ -41,12 +36,11 @@ const defaultProps = {
};
-class SqlEditor extends React.Component {
+class SqlEditor extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
autorun: props.queryEditor.autorun,
- sql: props.queryEditor.sql,
ctas: '',
};
}
@@ -82,16 +76,12 @@ class SqlEditor extends React.Component {
createTableAs() {
this.startQuery(true, true);
}
- textChange(text) {
- this.setState({ sql: text });
- this.props.actions.queryEditorSetSql(this.props.queryEditor, text);
+ setQueryEditorSql(sql) {
+ this.props.actions.queryEditorSetSql(this.props.queryEditor, sql);
}
- ctasChange() {}
- visualize() {}
ctasChanged(event) {
this.setState({ ctas: event.target.value });
}
-
sqlEditorHeight() {
// quick hack to make the white bg of the tab stretch full height.
const tabNavHeight = 40;
@@ -110,7 +100,7 @@ class SqlEditor extends React.Component {
style={{ width: '100px' }}
onClick={this.runQuery.bind(this, false)}
disabled={!(this.props.queryEditor.dbId)}
- key={shortid.generate()}
+ key="run-btn"
>
Run Query
@@ -124,7 +114,7 @@ class SqlEditor extends React.Component {
style={{ width: '100px' }}
onClick={this.runQuery.bind(this, true)}
disabled={!(this.props.queryEditor.dbId)}
- key={shortid.generate()}
+ key="run-async-btn"
>
Run Async
@@ -217,18 +207,9 @@ class SqlEditor extends React.Component {
/>
-
{editorBottomBar}
diff --git a/caravel/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx b/caravel/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx
index 32cd085b5e451..92844ac1b472c 100644
--- a/caravel/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/SqlEditorLeftBar.jsx
@@ -1,6 +1,5 @@
const $ = window.$ = require('jquery');
import React from 'react';
-
import Select from 'react-select';
import { Label, Button } from 'react-bootstrap';
import TableElement from './TableElement';
@@ -19,7 +18,7 @@ const defaultProps = {
actions: {},
};
-class SqlEditorLeftBar extends React.Component {
+class SqlEditorLeftBar extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
@@ -27,6 +26,7 @@ class SqlEditorLeftBar extends React.Component {
schemaOptions: [],
tableLoading: false,
tableOptions: [],
+ networkOn: true,
};
}
componentWillMount() {
@@ -92,8 +92,8 @@ class SqlEditorLeftBar extends React.Component {
this.setState({ tableLoading: true });
$.get(url, (data) => {
this.props.actions.mergeTable(Object.assign(data, {
- dbId: this.props.queryEditor.dbId,
- queryEditorId: this.props.queryEditor.id,
+ dbId: qe.dbId,
+ queryEditorId: qe.id,
schema: qe.schema,
expanded: true,
}));
@@ -163,7 +163,6 @@ class SqlEditorLeftBar extends React.Component {
isLoading={this.state.tableLoading}
placeholder={`Add a table (${this.state.tableOptions.length})`}
autosize={false}
- value={this.state.tableName}
onChange={this.changeTable.bind(this)}
options={this.state.tableOptions}
/>
@@ -173,7 +172,6 @@ class SqlEditorLeftBar extends React.Component {
{this.props.tables.map((table) => (
diff --git a/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx b/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx
index be01991424793..43d126b119237 100644
--- a/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx
@@ -23,7 +23,7 @@ const defaultProps = {
let queryCount = 1;
-class TabbedSqlEditors extends React.Component {
+class TabbedSqlEditors extends React.PureComponent {
constructor(props) {
super(props);
const uri = window.location.toString();
diff --git a/caravel/assets/javascripts/SqlLab/components/TableElement.jsx b/caravel/assets/javascripts/SqlLab/components/TableElement.jsx
index 3a5a142a3b07a..6be4451aee1bd 100644
--- a/caravel/assets/javascripts/SqlLab/components/TableElement.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/TableElement.jsx
@@ -9,7 +9,6 @@ import ModalTrigger from '../../components/ModalTrigger';
const propTypes = {
table: React.PropTypes.object,
- queryEditor: React.PropTypes.object,
actions: React.PropTypes.object,
};
@@ -18,7 +17,7 @@ const defaultProps = {
actions: {},
};
-class TableElement extends React.Component {
+class TableElement extends React.PureComponent {
popSelectStar() {
const qe = {
@@ -46,7 +45,7 @@ class TableElement extends React.Component {
}
dataPreviewModal() {
const query = {
- dbId: this.props.queryEditor.dbId,
+ dbId: this.props.table.dbId,
sql: this.props.table.selectStar,
tableName: this.props.table.name,
sqlEditorId: null,
diff --git a/caravel/assets/javascripts/SqlLab/components/Timer.jsx b/caravel/assets/javascripts/SqlLab/components/Timer.jsx
index b99c1db7304ec..d179b1c5ca74a 100644
--- a/caravel/assets/javascripts/SqlLab/components/Timer.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/Timer.jsx
@@ -3,7 +3,7 @@ import { now, fDuration } from '../../modules/dates';
import { STATE_BSSTYLE_MAP } from '../common.js';
-class Timer extends React.Component {
+class Timer extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
diff --git a/caravel/assets/javascripts/SqlLab/components/VisualizeModal.jsx b/caravel/assets/javascripts/SqlLab/components/VisualizeModal.jsx
index b28d1e60dc505..8a6a8d9077030 100644
--- a/caravel/assets/javascripts/SqlLab/components/VisualizeModal.jsx
+++ b/caravel/assets/javascripts/SqlLab/components/VisualizeModal.jsx
@@ -23,7 +23,7 @@ const defaultProps = {
onHide: () => {},
};
-class VisualizeModal extends React.Component {
+class VisualizeModal extends React.PureComponent {
constructor(props) {
super(props);
const uniqueId = shortid.generate();
diff --git a/caravel/assets/javascripts/reduxUtils.js b/caravel/assets/javascripts/reduxUtils.js
index fda1c8de3c986..c642c3d1018b0 100644
--- a/caravel/assets/javascripts/reduxUtils.js
+++ b/caravel/assets/javascripts/reduxUtils.js
@@ -74,3 +74,23 @@ export function enhancer() {
}
return enhancerWithPersistState;
}
+
+export function areArraysShallowEqual(arr1, arr2) {
+ // returns whether 2 arrays are shallow equal
+ // used in shouldComponentUpdate when denormalizing arrays
+ // where the array object is different every time, but the content might
+ // be the same
+ if (!arr1 || !arr2) {
+ return false;
+ }
+ if (arr1.length !== arr2.length) {
+ return false;
+ }
+ const length = arr1.length;
+ for (let i = 0; i < length; i++) {
+ if (arr1[i] !== arr2[i]) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/caravel/assets/package.json b/caravel/assets/package.json
index d91bf5736e94e..433c3f4367abe 100644
--- a/caravel/assets/package.json
+++ b/caravel/assets/package.json
@@ -10,7 +10,7 @@
"scripts": {
"test": "mocha --compilers js:babel-core/register --require spec/helpers/browser.js --recursive spec/**/*_spec.*",
"cover": "babel-node ./node_modules/.bin/istanbul cover _mocha -- --require spec/helpers/browser.js --recursive spec/**/*_spec.*",
- "dev": "NODE_ENV=dev webpack -d --watch --colors --progress",
+ "dev": "NODE_ENV=dev webpack --watch --colors --progress --debug --devtool cheap-eval-source-map --output-pathinfo",
"prod": "NODE_ENV=production node --max_old_space_size=8192 ./node_modules/webpack/bin/webpack.js -p --colors --progress",
"build": "NODE_ENV=production webpack --colors --progress",
"lint": "eslint --ignore-path=.eslintignore --ext .js,.jsx ."