diff --git a/desktop/plugins/public/databases/ButtonNavigation.tsx b/desktop/plugins/public/databases/ButtonNavigation.tsx index 27550dd1d..5e7e52a05 100644 --- a/desktop/plugins/public/databases/ButtonNavigation.tsx +++ b/desktop/plugins/public/databases/ButtonNavigation.tsx @@ -10,40 +10,42 @@ import {Button, ButtonGroup, Glyph, colors} from 'flipper'; import React from 'react'; -export default function ButtonNavigation(props: { - /** Back button is enabled */ - canGoBack: boolean; - /** Forwards button is enabled */ - canGoForward: boolean; - /** Callback when back button is clicked */ - onBack: () => void; - /** Callback when forwards button is clicked */ - onForward: () => void; -}) { - return ( - - - - - ); -} +export default React.memo( + (props: { + /** Back button is enabled */ + canGoBack: boolean; + /** Forwards button is enabled */ + canGoForward: boolean; + /** Callback when back button is clicked */ + onBack: () => void; + /** Callback when forwards button is clicked */ + onForward: () => void; + }) => { + return ( + + + + + ); + }, +); diff --git a/desktop/plugins/public/databases/index.tsx b/desktop/plugins/public/databases/index.tsx index 3bab753aa..d043c4626 100644 --- a/desktop/plugins/public/databases/index.tsx +++ b/desktop/plugins/public/databases/index.tsx @@ -29,6 +29,7 @@ import { TableRowSortOrder, Value, renderValue, + TableHighlightedRows, } from 'flipper'; import React, {KeyboardEvent, ChangeEvent, useState, useCallback} from 'react'; import {Methods, Events} from './ClientProtocol'; @@ -143,7 +144,7 @@ function transformRow( return {key: String(index), columns: transformedColumns}; } -function renderQueryHistory(history: Array) { +const QueryHistory = React.memo(({history}: {history: Array}) => { if (!history || typeof history === 'undefined') { return null; } @@ -181,7 +182,7 @@ function renderQueryHistory(history: Array) { /> ); -} +}); type PageInfoProps = { currentRow: number; @@ -190,27 +191,33 @@ type PageInfoProps = { onChange: (currentRow: number, count: number) => void; }; -function PageInfo(props: PageInfoProps) { +const PageInfo = React.memo((props: PageInfoProps) => { const [state, setState] = useState({ isOpen: false, inputValue: String(props.currentRow), }); - const onOpen = () => { + const onOpen = useCallback(() => { setState({...state, isOpen: true}); - }; + }, [state]); - const onInputChanged = (e: ChangeEvent) => { - setState({...state, inputValue: e.target.value}); - }; + const onInputChanged = useCallback( + (e: ChangeEvent) => { + setState({...state, inputValue: e.target.value}); + }, + [state], + ); - const onSubmit = (e: KeyboardEvent) => { - if (e.key === 'Enter') { - const rowNumber = parseInt(state.inputValue, 10); - props.onChange(rowNumber - 1, props.count); - setState({...state, isOpen: false}); - } - }; + const onSubmit = useCallback( + (e: KeyboardEvent) => { + if (e.key === 'Enter') { + const rowNumber = parseInt(state.inputValue, 10); + props.onChange(rowNumber - 1, props.count); + setState({...state, isOpen: false}); + } + }, + [props, state], + ); return ( @@ -236,7 +243,135 @@ function PageInfo(props: PageInfoProps) { )} ); -} +}); + +const DataTable = React.memo( + ({ + page, + highlightedRowsChanged, + sortOrderChanged, + currentSort, + currentStructure, + onRowEdited, + }: { + page: Page | null; + highlightedRowsChanged: (highlightedRows: TableHighlightedRows) => void; + sortOrderChanged: (sortOrder: TableRowSortOrder) => void; + currentSort: TableRowSortOrder | null; + currentStructure: Structure | null; + onRowEdited: (changes: {[key: string]: string | null}) => void; + }) => + page ? ( + + ({ + key: name, + visible: true, + }))} + columns={page.columns.reduce( + (acc, val) => + Object.assign({}, acc, { + [val]: {value: val, resizable: true, sortable: true}, + }), + {}, + )} + zebra={true} + rows={page.rows.map((row: Array, index: number) => + transformRow(page.columns, row, index), + )} + horizontallyScrollable={true} + multiHighlight={true} + onRowHighlighted={highlightedRowsChanged} + onSort={sortOrderChanged} + initialSortOrder={currentSort ?? undefined} + /> + {page.highlightedRows.length === 1 && ( + + )} + + ) : null, +); + +const QueryTable = React.memo( + ({ + query, + highlightedRowsChanged, + }: { + query: QueryResult | null; + highlightedRowsChanged: (highlightedRows: TableHighlightedRows) => void; + }) => { + if (!query || query === null) { + return null; + } + if ( + query.table && + typeof query.table !== 'undefined' && + query.table !== null + ) { + const table = query.table; + const columns = table.columns; + const rows = table.rows; + return ( + + ({ + key: name, + visible: true, + }))} + columns={columns.reduce( + (acc, val) => + Object.assign({}, acc, {[val]: {value: val, resizable: true}}), + {}, + )} + zebra={true} + rows={rows.map((row: Array, index: number) => + transformRow(columns, row, index), + )} + horizontallyScrollable={true} + onRowHighlighted={highlightedRowsChanged} + /> + {table.highlightedRows.length === 1 && ( + + )} + + ); + } else if (query.id && query.id !== null) { + return ( + + + Row id: {query.id} + + + ); + } else if (query.count && query.count !== null) { + return ( + + + Rows affected: {query.count} + + + ); + } else { + return null; + } + }, +); export function plugin(client: PluginClient) { const pluginState = createState({ @@ -523,6 +658,26 @@ export function plugin(client: PluginClient) { }); }; + const pageHighlightedRowsChanged = (event: TableHighlightedRows) => { + pluginState.update((draftState: DatabasesPluginState) => { + if (draftState.currentPage !== null) { + draftState.currentPage.highlightedRows = event.map(parseInt); + } + }); + }; + + const queryHighlightedRowsChanged = (event: TableHighlightedRows) => { + pluginState.update((state) => { + if (state.queryResult) { + if (state.queryResult.table) { + state.queryResult.table.highlightedRows = event.map(parseInt); + } + state.queryResult.id = null; + state.queryResult.count = null; + } + }); + }; + pluginState.subscribe( (newState: DatabasesPluginState, previousState: DatabasesPluginState) => { const databaseId = newState.selectedDatabase; @@ -651,6 +806,8 @@ export function plugin(client: PluginClient) { updateFavorites, sortByChanged, updateQuery, + pageHighlightedRowsChanged, + queryHighlightedRowsChanged, }; } @@ -755,6 +912,27 @@ export function Component() { [instance], ); + const pageHighlightedRowsChanged = useCallback( + (rows: TableHighlightedRows) => { + instance.pageHighlightedRowsChanged(rows); + }, + [instance], + ); + + const queryHighlightedRowsChanged = useCallback( + (rows: TableHighlightedRows) => { + instance.queryHighlightedRowsChanged(rows); + }, + [instance], + ); + + const sortOrderChanged = useCallback( + (sortOrder: TableRowSortOrder) => { + instance.sortByChanged({sortOrder}); + }, + [instance], + ); + const onRowEdited = useCallback( (change: {[key: string]: string | null}) => { const { @@ -858,142 +1036,6 @@ export function Component() { [instance], ); - const renderTable = (page: Page | null) => { - if (!page) { - return null; - } - return ( - - ({ - key: name, - visible: true, - }))} - columns={page.columns.reduce( - (acc, val) => - Object.assign({}, acc, { - [val]: {value: val, resizable: true, sortable: true}, - }), - {}, - )} - zebra={true} - rows={page.rows.map((row: Array, index: number) => - transformRow(page.columns, row, index), - )} - horizontallyScrollable={true} - multiHighlight={true} - onRowHighlighted={(highlightedRows) => - instance.state.update((draftState: DatabasesPluginState) => { - if (draftState.currentPage !== null) { - draftState.currentPage.highlightedRows = highlightedRows.map( - parseInt, - ); - } - }) - } - onSort={(sortOrder: TableRowSortOrder) => { - instance.sortByChanged({ - sortOrder, - }); - }} - initialSortOrder={state.currentSort ?? undefined} - /> - {page.highlightedRows.length === 1 && ( - - )} - - ); - }; - - const renderQuery = (query: QueryResult | null) => { - if (!query || query === null) { - return null; - } - if ( - query.table && - typeof query.table !== 'undefined' && - query.table !== null - ) { - const table = query.table; - const columns = table.columns; - const rows = table.rows; - return ( - - ({ - key: name, - visible: true, - }))} - columns={columns.reduce( - (acc, val) => - Object.assign({}, acc, {[val]: {value: val, resizable: true}}), - {}, - )} - zebra={true} - rows={rows.map((row: Array, index: number) => - transformRow(columns, row, index), - )} - horizontallyScrollable={true} - onRowHighlighted={(highlightedRows) => { - instance.state.set({ - ...instance.state.get(), - queryResult: { - table: { - columns: columns, - rows: rows, - highlightedRows: highlightedRows.map(parseInt), - }, - id: null, - count: null, - }, - }); - }} - /> - {table.highlightedRows.length === 1 && ( - - )} - - ); - } else if (query.id && query.id !== null) { - return ( - - - Row id: {query.id} - - - ); - } else if (query.count && query.count !== null) { - return ( - - - Rows affected: {query.count} - - - ); - } else { - return null; - } - }; - const tableOptions = (state.selectedDatabase && state.databases[state.selectedDatabase - 1] && @@ -1152,20 +1194,34 @@ export function Component() { ) : null} - {state.viewMode === 'data' ? renderTable(state.currentPage) : null} + {state.viewMode === 'data' ? ( + + ) : null} {state.viewMode === 'structure' ? ( ) : null} - {state.viewMode === 'SQL' ? renderQuery(state.queryResult) : null} + {state.viewMode === 'SQL' ? ( + + ) : null} {state.viewMode === 'tableInfo' ? ( ) : null} - {state.viewMode === 'queryHistory' - ? renderQueryHistory(state.queryHistory) - : null} + {state.viewMode === 'queryHistory' ? ( + + ) : null}