From 0c5415a5747c3357ab546e791fa58ffe97b7bfd3 Mon Sep 17 00:00:00 2001 From: Douglas Muraoka Date: Thu, 22 Aug 2019 16:31:17 -0300 Subject: [PATCH 1/6] fix(Database Browser): Avoid unnecessary rendering during navigation --- .../BrowserCell/BrowserCell.react.js | 218 ++++++++++-------- src/components/BrowserRow/BrowserRow.react.js | 84 +++++++ .../Data/Browser/BrowserTable.react.js | 99 +++----- .../Data/Browser/DataBrowser.react.js | 18 +- 4 files changed, 250 insertions(+), 169 deletions(-) create mode 100644 src/components/BrowserRow/BrowserRow.react.js diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js index 3d9259b25c..19bf9bc6db 100644 --- a/src/components/BrowserCell/BrowserCell.react.js +++ b/src/components/BrowserCell/BrowserCell.react.js @@ -9,115 +9,133 @@ import { dateStringUTC } from 'lib/DateUtils'; import getFileName from 'lib/getFileName'; import Parse from 'parse'; import Pill from 'components/Pill/Pill.react'; -import React from 'react'; +import React, { Component } from 'react'; import styles from 'components/BrowserCell/BrowserCell.scss'; import { unselectable } from 'stylesheets/base.scss'; -let BrowserCell = ({ type, value, hidden, width, current, onSelect, onEditChange, setRelation, onPointerClick }) => { - let content = value; - let classes = [styles.cell, unselectable]; - if (hidden) { - content = '(hidden)'; - classes.push(styles.empty); - } else if (value === undefined) { - if (type === 'ACL') { - content = 'Public Read + Write'; - } else { - content = '(undefined)'; - classes.push(styles.empty); - } - } else if (value === null) { - content = '(null)'; - classes.push(styles.empty); - } else if (value === '') { - content =  ; - classes.push(styles.empty); - } else if (type === 'Pointer') { - if (value && value.__type) { - const object = new Parse.Object(value.className); - object.id = value.objectId; - value = object; +export default class BrowserCell extends Component { + shouldComponentUpdate(nextProps) { + const shallowVerifyProps = ['current', 'value', 'width', 'col', 'row', 'type', 'hidden']; + if (shallowVerifyProps.some(propName => this.props[propName] !== nextProps[propName])) { + return true; } - content = ( - - - - ); - } else if (type === 'Date') { - if (typeof value === 'object' && value.__type) { - value = new Date(value.iso); - } else if (typeof value === 'string') { - value = new Date(value); + const { value } = this.props; + const { value: nextValue } = nextProps; + if (typeof value !== typeof nextValue) { + return true; } - content = dateStringUTC(value); - } else if (type === 'Boolean') { - content = value ? 'True' : 'False'; - } else if (type === 'Object' || type === 'Bytes' || type === 'Array') { - content = JSON.stringify(value); - } else if (type === 'File') { - if (value.url()) { - content = ; - } else { - content = ; + const isRefDifferent = value !== nextValue; + if (isRefDifferent && typeof value === 'object') { + return JSON.stringify(value) !== JSON.stringify(nextValue); } - } else if (type === 'ACL') { - let pieces = []; - let json = value.toJSON(); - if (Object.prototype.hasOwnProperty.call(json, '*')) { - if (json['*'].read && json['*'].write) { - pieces.push('Public Read + Write'); - } else if (json['*'].read) { - pieces.push('Public Read'); - } else if (json['*'].write) { - pieces.push('Public Write'); + return isRefDifferent; + } + + render() { + const { type, value, hidden, width, current, onSelect, onEditChange, setRelation, onPointerClick, row, col } = this.props; + let content = value; + let classes = [styles.cell, unselectable]; + if (hidden) { + content = '(hidden)'; + classes.push(styles.empty); + } else if (value === undefined) { + if (type === 'ACL') { + content = 'Public Read + Write'; + } else { + content = '(undefined)'; + classes.push(styles.empty); } - } - for (let role in json) { - if (role !== '*') { - pieces.push(role); + } else if (value === null) { + content = '(null)'; + classes.push(styles.empty); + } else if (value === '') { + content =  ; + classes.push(styles.empty); + } else if (type === 'Pointer') { + if (value && value.__type) { + const object = new Parse.Object(value.className); + object.id = value.objectId; + value = object; + } + content = ( + + + + ); + } else if (type === 'Date') { + if (typeof value === 'object' && value.__type) { + value = new Date(value.iso); + } else if (typeof value === 'string') { + value = new Date(value); + } + content = dateStringUTC(value); + } else if (type === 'Boolean') { + content = value ? 'True' : 'False'; + } else if (type === 'Object' || type === 'Bytes' || type === 'Array') { + content = JSON.stringify(value); + } else if (type === 'File') { + if (value.url()) { + content = ; + } else { + content = ; + } + } else if (type === 'ACL') { + let pieces = []; + let json = value.toJSON(); + if (Object.prototype.hasOwnProperty.call(json, '*')) { + if (json['*'].read && json['*'].write) { + pieces.push('Public Read + Write'); + } else if (json['*'].read) { + pieces.push('Public Read'); + } else if (json['*'].write) { + pieces.push('Public Write'); + } + } + for (let role in json) { + if (role !== '*') { + pieces.push(role); + } } + if (pieces.length === 0) { + pieces.push('Master Key Only'); + } + content = pieces.join(', '); + } else if (type === 'GeoPoint') { + content = `(${value.latitude}, ${value.longitude})`; + } else if (type === 'Polygon') { + content = value.coordinates.map(coord => `(${coord})`) + } else if (type === 'Relation') { + content = ( +
+ setRelation(value)} value='View relation' /> +
+ ); } - if (pieces.length === 0) { - pieces.push('Master Key Only'); + + if (current) { + classes.push(styles.current); } - content = pieces.join(', '); - } else if (type === 'GeoPoint') { - content = `(${value.latitude}, ${value.longitude})`; - } else if (type === 'Polygon') { - content = value.coordinates.map(coord => `(${coord})`) - } else if (type === 'Relation') { - content = ( -
- setRelation(value)} value='View relation' /> -
+ return ( + onSelect({ row, col })} + onDoubleClick={() => { + if (type !== 'Relation') { + onEditChange(true) + } + }} + onTouchEnd={e => { + if (current && type !== 'Relation') { + // The touch event may trigger an unwanted change in the column value + if (['ACL', 'Boolean', 'File'].includes(type)) { + e.preventDefault(); + } + onEditChange(true); + } + }}> + {content} + ); } - - if (current) { - classes.push(styles.current); - } - return ( - { - if (type !== 'Relation') { - onEditChange(true) - } - }} - onTouchEnd={e => { - if (current && type !== 'Relation') { - // The touch event may trigger an unwanted change in the column value - if (['ACL', 'Boolean', 'File'].includes(type)) { - e.preventDefault(); - } - onEditChange(true); - } - }}> - {content} - - ); -}; - -export default BrowserCell; +} \ No newline at end of file diff --git a/src/components/BrowserRow/BrowserRow.react.js b/src/components/BrowserRow/BrowserRow.react.js new file mode 100644 index 0000000000..d8c17f373c --- /dev/null +++ b/src/components/BrowserRow/BrowserRow.react.js @@ -0,0 +1,84 @@ +import Parse from 'parse'; +import React, { Component } from 'react'; + +import BrowserCell from 'components/BrowserCell/BrowserCell.react'; +import styles from 'dashboard/Data/Browser/Browser.scss'; + +export default class BrowserRow extends Component { + shouldComponentUpdate(nextProps) { + const shallowVerifyProps = ['currentCol', 'rowWidth', 'selection', 'order', 'className', 'columns', 'isUnique', 'row', 'readOnlyFields']; + if (shallowVerifyProps.some(propName => this.props[propName] !== nextProps[propName])) { + return true; + } + const { obj } = this.props; + const { obj: nextObj } = nextProps; + if (typeof obj !== typeof nextObj) { + return true; + } + const isRefDifferent = obj !== nextObj; + if (isRefDifferent && typeof obj === 'object') { + return JSON.stringify(obj) !== JSON.stringify(nextObj); + } + return isRefDifferent; + } + + render() { + const { className, columns, currentCol, isUnique, obj, onPointerClick, order, readOnlyFields, row, rowWidth, selection, selectRow, setCurrent, setEditing, setRelation } = this.props; + let attributes = obj.attributes; + return ( +
+ + selectRow(obj.id, e.target.checked)} /> + + {order.map(({ name, width }, j) => { + let type = columns[name].type; + let attr = obj; + if (!isUnique) { + attr = attributes[name]; + if (name === 'objectId') { + attr = obj.id; + } else if (name === 'ACL' && className === '_User' && !attr) { + attr = new Parse.ACL({ '*': { read: true }, [obj.id]: { read: true, write: true }}); + } else if (type === 'Relation' && !attr && obj.id) { + attr = new Parse.Relation(obj, name); + attr.targetClassName = columns[name].targetClass; + } else if (type === 'Array' || type === 'Object') { + // This is needed to avoid unwanted conversions of objects to Parse.Objects. + // "Parse._encoding" is responsible to convert Parse data into raw data. + // Since array and object are generic types, we want to render them the way + // they were stored in the database. + attr = Parse._encode(obj.get(name)); + } + } + let hidden = false; + if (name === 'password' && className === '_User') { + hidden = true; + } else if (name === 'sessionToken') { + if (className === '_User' || className === '_Session') { + hidden = true; + } + } + return ( + -1} + width={width} + current={currentCol === j} + onSelect={setCurrent} + onEditChange={setEditing} + onPointerClick={onPointerClick} + setRelation={setRelation} + value={attr} + hidden={hidden} /> + ); + })} +
+ ); + } +} \ No newline at end of file diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index 21d70ade19..8967498188 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -5,7 +5,7 @@ * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. */ -import BrowserCell from 'components/BrowserCell/BrowserCell.react'; +import BrowserRow from 'components/BrowserRow/BrowserRow.react'; import * as browserUtils from 'lib/browserUtils'; import DataBrowserHeaderBar from 'components/DataBrowserHeaderBar/DataBrowserHeaderBar.react'; import Editor from 'dashboard/Data/Browser/Editor.react'; @@ -78,65 +78,6 @@ export default class BrowserTable extends React.Component { }); } - renderRow({ row, obj, rowWidth }) { - let attributes = obj.attributes; - let index = row - this.state.offset; - return ( -
- - this.props.selectRow(obj.id, e.target.checked)} /> - - {this.props.order.map(({ name, width }, j) => { - let type = this.props.columns[name].type; - let attr = obj; - if (!this.props.isUnique) { - attr = attributes[name]; - if (name === 'objectId') { - attr = obj.id; - } else if (name === 'ACL' && this.props.className === '_User' && !attr) { - attr = new Parse.ACL({ '*': { read: true }, [obj.id]: { read: true, write: true }}); - } else if (type === 'Relation' && !attr && obj.id) { - attr = new Parse.Relation(obj, name); - attr.targetClassName = this.props.columns[name].targetClass; - } else if (type === 'Array' || type === 'Object') { - // This is needed to avoid unwanted conversions of objects to Parse.Objects. - // "Parse._encoding" is responsible to convert Parse data into raw data. - // Since array and object are generic types, we want to render them the way - // they were stored in the database. - attr = Parse._encode(obj.get(name)); - } - } - let current = this.props.current && this.props.current.row === row && this.props.current.col === j; - let hidden = false; - if (name === 'password' && this.props.className === '_User') { - hidden = true; - } else if (name === 'sessionToken') { - if (this.props.className === '_User' || this.props.className === '_Session') { - hidden = true; - } - } - return ( - -1} - width={width} - current={current} - onSelect={() => this.props.setCurrent({ row: row, col: j })} - onEditChange={(state) => this.props.setEditing(state)} - onPointerClick={this.props.onPointerClick} - setRelation={this.props.setRelation} - value={attr} - hidden={hidden} /> - ); - })} -
- ); - } - render() { let ordering = {}; if (this.props.ordering) { @@ -165,9 +106,26 @@ export default class BrowserTable extends React.Component { } let newRow = null; if (this.props.newObject && this.state.offset <= 0) { + const currentCol = this.props.current && this.props.current.row === -1 ? this.props.current.col : undefined; newRow = (
- {this.renderRow({ row: -1, obj: this.props.newObject, json: {}, rowWidth: rowWidth })} +
); } @@ -176,7 +134,24 @@ export default class BrowserTable extends React.Component { for (let i = this.state.offset; i < end; i++) { let index = i - this.state.offset; let obj = this.props.data[i]; - rows[index] = this.renderRow({ row: i, obj, rowWidth: rowWidth }); + const currentCol = this.props.current && this.props.current.row === i ? this.props.current.col : undefined; + rows[index] = } if (this.props.editing) { diff --git a/src/dashboard/Data/Browser/DataBrowser.react.js b/src/dashboard/Data/Browser/DataBrowser.react.js index bed0253267..685c937a9b 100644 --- a/src/dashboard/Data/Browser/DataBrowser.react.js +++ b/src/dashboard/Data/Browser/DataBrowser.react.js @@ -35,6 +35,10 @@ export default class DataBrowser extends React.Component { }; this.handleKey = this.handleKey.bind(this); + this.handleHeaderDragDrop = this.handleHeaderDragDrop.bind(this); + this.handleResize = this.handleResize.bind(this); + this.setCurrent = this.setCurrent.bind(this); + this.setEditing = this.setEditing.bind(this); this.saveOrderTimeout = null; } @@ -183,8 +187,8 @@ export default class DataBrowser extends React.Component { } setCurrent(current) { - if (this.state.current !== current) { - this.setState({ current: current }); + if (JSON.stringify(this.state.current) !== JSON.stringify(current)) { + this.setState({ current }); } } @@ -198,16 +202,16 @@ export default class DataBrowser extends React.Component { current={this.state.current} editing={this.state.editing} className={className} - handleHeaderDragDrop={this.handleHeaderDragDrop.bind(this)} - handleResize={this.handleResize.bind(this)} - setEditing={this.setEditing.bind(this)} - setCurrent={this.setCurrent.bind(this)} + handleHeaderDragDrop={this.handleHeaderDragDrop} + handleResize={this.handleResize} + setEditing={this.setEditing} + setCurrent={this.setCurrent} {...other} /> Date: Thu, 22 Aug 2019 16:32:03 -0300 Subject: [PATCH 2/6] fix(Database Browser): Improve scrolling UX --- src/dashboard/Data/Browser/BrowserTable.react.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index 8967498188..df00e7fc36 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -18,7 +18,7 @@ import Button from 'components/Button/Button.react'; import ParseApp from 'lib/ParseApp'; import PropTypes from 'lib/PropTypes'; -const MAX_ROWS = 60; // Number of rows to render at any time +const MAX_ROWS = 200; // Number of rows to render at any time const ROW_HEIGHT = 31; const READ_ONLY = [ 'objectId', 'createdAt', 'updatedAt' ]; @@ -61,16 +61,17 @@ export default class BrowserTable extends React.Component { return; } requestAnimationFrame(() => { - let rowsAbove = Math.floor(this.refs.table.scrollTop / ROW_HEIGHT); + const currentScrollTop = this.refs.table.scrollTop; + const rowsAbove = Math.floor(currentScrollTop / ROW_HEIGHT); let offset = this.state.offset; - if (rowsAbove - this.state.offset > 20) { + if (rowsAbove - this.state.offset > 160) { offset = Math.floor(rowsAbove / 10) * 10 - 10; } else if (rowsAbove - this.state.offset < 10) { offset = Math.max(0, Math.floor(rowsAbove / 10) * 10 - 30); } if (this.state.offset !== offset) { this.setState({ offset }); - this.refs.table.scrollTop = rowsAbove * ROW_HEIGHT; + this.refs.table.scrollTop = currentScrollTop; } if (this.props.maxFetched - offset < 100) { this.props.fetchNextPage(); From 84877439fe2cea24c73caed3b54c3790ea493bcd Mon Sep 17 00:00:00 2001 From: Douglas Muraoka Date: Thu, 22 Aug 2019 20:28:15 -0300 Subject: [PATCH 3/6] Improve scroll handler readability --- .../Data/Browser/BrowserTable.react.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index df00e7fc36..40fb6859c4 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -19,6 +19,7 @@ import ParseApp from 'lib/ParseApp'; import PropTypes from 'lib/PropTypes'; const MAX_ROWS = 200; // Number of rows to render at any time +const ROWS_OFFSET = 160; const ROW_HEIGHT = 31; const READ_ONLY = [ 'objectId', 'createdAt', 'updatedAt' ]; @@ -62,12 +63,19 @@ export default class BrowserTable extends React.Component { } requestAnimationFrame(() => { const currentScrollTop = this.refs.table.scrollTop; - const rowsAbove = Math.floor(currentScrollTop / ROW_HEIGHT); + let rowsAbove = Math.floor(currentScrollTop / ROW_HEIGHT); let offset = this.state.offset; - if (rowsAbove - this.state.offset > 160) { - offset = Math.floor(rowsAbove / 10) * 10 - 10; - } else if (rowsAbove - this.state.offset < 10) { - offset = Math.max(0, Math.floor(rowsAbove / 10) * 10 - 30); + const currentRow = rowsAbove - this.state.offset; + + // If the scroll is near the beginning or end of the offset, + // we need to update the table data with the previous/next offset + if (currentRow < 10 || currentRow > ROWS_OFFSET + 10) { + // Rounds the number of rows above + rowsAbove = Math.floor(rowsAbove / 10) * 10; + + offset = currentRow < 10 + ? Math.max(0, rowsAbove - ROWS_OFFSET) // Previous set of rows + : rowsAbove - 10; // Next set of rows } if (this.state.offset !== offset) { this.setState({ offset }); From 2b10b2f8966be2876167be7b99346f0c6a4f517a Mon Sep 17 00:00:00 2001 From: Douglas Muraoka Date: Thu, 22 Aug 2019 20:36:31 -0300 Subject: [PATCH 4/6] fix(Database Browser): Improve initial rendering --- .../BrowserCell/BrowserCell.react.js | 3 ++- src/components/BrowserRow/BrowserRow.react.js | 11 +++----- src/dashboard/Data/Browser/Browser.react.js | 27 +++++-------------- .../Data/Browser/BrowserToolbar.react.js | 20 ++++++++++++-- .../Data/Browser/DataBrowser.react.js | 16 ++++++++++- 5 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js index 19bf9bc6db..b06c7b0cc1 100644 --- a/src/components/BrowserCell/BrowserCell.react.js +++ b/src/components/BrowserCell/BrowserCell.react.js @@ -15,7 +15,8 @@ import { unselectable } from 'stylesheets/base.scss'; export default class BrowserCell extends Component { shouldComponentUpdate(nextProps) { - const shallowVerifyProps = ['current', 'value', 'width', 'col', 'row', 'type', 'hidden']; + const shallowVerifyProps = [...new Set(Object.keys(this.props).concat(Object.keys(nextProps)))] + .filter(propName => propName !== 'value'); if (shallowVerifyProps.some(propName => this.props[propName] !== nextProps[propName])) { return true; } diff --git a/src/components/BrowserRow/BrowserRow.react.js b/src/components/BrowserRow/BrowserRow.react.js index d8c17f373c..8bbb8ae0b0 100644 --- a/src/components/BrowserRow/BrowserRow.react.js +++ b/src/components/BrowserRow/BrowserRow.react.js @@ -6,20 +6,15 @@ import styles from 'dashboard/Data/Browser/Browser.scss'; export default class BrowserRow extends Component { shouldComponentUpdate(nextProps) { - const shallowVerifyProps = ['currentCol', 'rowWidth', 'selection', 'order', 'className', 'columns', 'isUnique', 'row', 'readOnlyFields']; + const shallowVerifyProps = [...new Set(Object.keys(this.props).concat(Object.keys(nextProps)))] + .filter(propName => propName !== 'obj'); if (shallowVerifyProps.some(propName => this.props[propName] !== nextProps[propName])) { return true; } const { obj } = this.props; const { obj: nextObj } = nextProps; - if (typeof obj !== typeof nextObj) { - return true; - } const isRefDifferent = obj !== nextObj; - if (isRefDifferent && typeof obj === 'object') { - return JSON.stringify(obj) !== JSON.stringify(nextObj); - } - return isRefDifferent; + return isRefDifferent ? JSON.stringify(obj) !== JSON.stringify(nextObj) : isRefDifferent; } render() { diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index b4e7bcd284..dcbe2baaec 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -910,13 +910,6 @@ class Browser extends DashboardView { ); } else if (className && classes.get(className)) { - let schema = {}; - classes.get(className).forEach(({ type, targetClass }, col) => { - schema[col] = { - type, - targetClass, - }; - }); let columns = { objectId: { type: 'String' } @@ -924,20 +917,13 @@ class Browser extends DashboardView { if (this.state.isUnique) { columns = {}; } - let userPointers = []; - classes.get(className).forEach((field, name) => { - if (name === 'objectId') { - return; - } - if (this.state.isUnique && name !== this.state.uniqueField) { + classes.get(className).forEach(({ type, targetClass }, name) => { + if (name === 'objectId' || this.state.isUnique && name !== this.state.uniqueField) { return; } - let info = { type: field.type }; - if (field.targetClass) { - info.targetClass = field.targetClass; - if (field.targetClass === '_User') { - userPointers.push(name); - } + const info = { type }; + if (targetClass) { + info.targetClass = targetClass; } columns[name] = info; }); @@ -958,8 +944,7 @@ class Browser extends DashboardView { uniqueField={this.state.uniqueField} count={count} perms={this.state.clp[className]} - schema={schema} - userPointers={userPointers} + schema={this.props.schema} filters={this.state.filters} onFilterChange={this.updateFilters} onRemoveColumn={this.showRemoveColumn} diff --git a/src/dashboard/Data/Browser/BrowserToolbar.react.js b/src/dashboard/Data/Browser/BrowserToolbar.react.js index 67fd5cc4b9..3d83f75e6d 100644 --- a/src/dashboard/Data/Browser/BrowserToolbar.react.js +++ b/src/dashboard/Data/Browser/BrowserToolbar.react.js @@ -22,7 +22,6 @@ let BrowserToolbar = ({ count, perms, schema, - userPointers, filters, selection, relation, @@ -42,6 +41,7 @@ let BrowserToolbar = ({ onRefresh, hidePerms, isUnique, + uniqueField, enableDeleteAllRows, enableExportClass, @@ -137,6 +137,22 @@ let BrowserToolbar = ({ classes.push(styles.toolbarButtonDisabled); onClick = null; } + + const userPointers = []; + const schemaSimplifiedData = {}; + schema.data.get('classes').get(className).forEach(({ type, targetClass }, col) => { + if (name === 'objectId' || isUnique && name !== uniqueField) { + return; + } + if (targetClass === '_User') { + userPointers.push(name); + } + schemaSimplifiedData[col] = { + type, + targetClass, + }; + }); + return (
diff --git a/src/dashboard/Data/Browser/DataBrowser.react.js b/src/dashboard/Data/Browser/DataBrowser.react.js index 685c937a9b..ae6df0b61c 100644 --- a/src/dashboard/Data/Browser/DataBrowser.react.js +++ b/src/dashboard/Data/Browser/DataBrowser.react.js @@ -43,6 +43,19 @@ export default class DataBrowser extends React.Component { this.saveOrderTimeout = null; } + shouldComponentUpdate(nextProps, nextState) { + const shallowVerifyStates = [...new Set(Object.keys(this.state).concat(Object.keys(nextState)))] + if (shallowVerifyStates.some(stateName => this.state[stateName] !== nextState[stateName])) { + return true; + } + const shallowVerifyProps = [...new Set(Object.keys(this.props).concat(Object.keys(nextProps)))] + .filter(propName => propName !== 'columns'); + if (shallowVerifyProps.some(propName => this.props[propName] !== nextProps[propName])) { + return true; + } + return JSON.stringify(this.props.columns) !== JSON.stringify(nextProps.columns); + } + componentWillReceiveProps(props, context) { if (props.className !== this.props.className) { let order = ColumnPreferences.getOrder( @@ -193,7 +206,7 @@ export default class DataBrowser extends React.Component { } render() { - let { className, ...other } = this.props; + let { className, count, ...other } = this.props; const { preventSchemaEdits } = this.context.currentApp; return (
@@ -208,6 +221,7 @@ export default class DataBrowser extends React.Component { setCurrent={this.setCurrent} {...other} /> Date: Fri, 23 Aug 2019 17:36:14 -0300 Subject: [PATCH 5/6] fix: Assign const --- src/components/BrowserCell/BrowserCell.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js index f3d07912f2..bfb6d9801a 100644 --- a/src/components/BrowserCell/BrowserCell.react.js +++ b/src/components/BrowserCell/BrowserCell.react.js @@ -58,7 +58,7 @@ export default class BrowserCell extends Component { } render() { - const { type, value, hidden, width, current, onSelect, onEditChange, setRelation, onPointerClick, row, col } = this.props; + let { type, value, hidden, width, current, onSelect, onEditChange, setRelation, onPointerClick, row, col } = this.props; let content = value; let classes = [styles.cell, unselectable]; if (hidden) { From 8d08e571856e1df179ea8a948295f222d8fad414 Mon Sep 17 00:00:00 2001 From: Douglas Muraoka Date: Mon, 26 Aug 2019 10:49:23 -0300 Subject: [PATCH 6/6] fix: Scroll not triggering data fetch --- src/dashboard/Data/Browser/BrowserTable.react.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index 5dc788de60..9ec2a39531 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -69,7 +69,7 @@ export default class BrowserTable extends React.Component { // If the scroll is near the beginning or end of the offset, // we need to update the table data with the previous/next offset - if (currentRow < 10 || currentRow > ROWS_OFFSET + 10) { + if (currentRow < 10 || currentRow >= ROWS_OFFSET) { // Rounds the number of rows above rowsAbove = Math.floor(rowsAbove / 10) * 10; @@ -81,7 +81,7 @@ export default class BrowserTable extends React.Component { this.setState({ offset }); this.refs.table.scrollTop = currentScrollTop; } - if (this.props.maxFetched - offset < 100) { + if (this.props.maxFetched - offset <= ROWS_OFFSET * 1.4) { this.props.fetchNextPage(); } });