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 ."