mirror of
https://github.com/apache/superset.git
synced 2026-04-22 01:24:43 +00:00
perf(sqllab): Rendering perf improvement using immutable state (#20877)
* perf(sqllab): Rendering perf improvement using immutable state - keep queryEditors immutable during active state - add unsavedQueryEditor to store all active changes - refactor each component to subscribe the related unsaved editor state only * revert ISaveableDatasource type cast * missing trigger prop * a default of an empty object and optional operator
This commit is contained in:
@@ -31,12 +31,28 @@ import {
|
||||
extendArr,
|
||||
} from '../../reduxUtils';
|
||||
|
||||
function alterUnsavedQueryEditorState(state, updatedState, id) {
|
||||
return {
|
||||
...(state.unsavedQueryEditor.id === id && state.unsavedQueryEditor),
|
||||
...(id ? { id, ...updatedState } : state.unsavedQueryEditor),
|
||||
};
|
||||
}
|
||||
|
||||
export default function sqlLabReducer(state = {}, action) {
|
||||
const actionHandlers = {
|
||||
[actions.ADD_QUERY_EDITOR]() {
|
||||
const tabHistory = state.tabHistory.slice();
|
||||
tabHistory.push(action.queryEditor.id);
|
||||
const newState = { ...state, tabHistory };
|
||||
const mergeUnsavedState = alterInArr(
|
||||
state,
|
||||
'queryEditors',
|
||||
state.unsavedQueryEditor,
|
||||
{
|
||||
...state.unsavedQueryEditor,
|
||||
},
|
||||
);
|
||||
const newState = {
|
||||
...mergeUnsavedState,
|
||||
tabHistory: [...state.tabHistory, action.queryEditor.id],
|
||||
};
|
||||
return addToArr(newState, 'queryEditors', action.queryEditor);
|
||||
},
|
||||
[actions.QUERY_EDITOR_SAVED]() {
|
||||
@@ -66,9 +82,14 @@ export default function sqlLabReducer(state = {}, action) {
|
||||
);
|
||||
},
|
||||
[actions.CLONE_QUERY_TO_NEW_TAB]() {
|
||||
const progenitor = state.queryEditors.find(
|
||||
const queryEditor = state.queryEditors.find(
|
||||
qe => qe.id === state.tabHistory[state.tabHistory.length - 1],
|
||||
);
|
||||
const progenitor = {
|
||||
...queryEditor,
|
||||
...(state.unsavedQueryEditor.id === queryEditor.id &&
|
||||
state.unsavedQueryEditor),
|
||||
};
|
||||
const qe = {
|
||||
remoteId: progenitor.remoteId,
|
||||
name: t('Copy of %s', progenitor.name),
|
||||
@@ -79,7 +100,14 @@ export default function sqlLabReducer(state = {}, action) {
|
||||
queryLimit: action.query.queryLimit,
|
||||
maxRow: action.query.maxRow,
|
||||
};
|
||||
return sqlLabReducer(state, actions.addQueryEditor(qe));
|
||||
const stateWithoutUnsavedState = {
|
||||
...state,
|
||||
unsavedQueryEditor: {},
|
||||
};
|
||||
return sqlLabReducer(
|
||||
stateWithoutUnsavedState,
|
||||
actions.addQueryEditor(qe),
|
||||
);
|
||||
},
|
||||
[actions.REMOVE_QUERY_EDITOR]() {
|
||||
let newState = removeFromArr(state, 'queryEditors', action.queryEditor);
|
||||
@@ -183,16 +211,20 @@ export default function sqlLabReducer(state = {}, action) {
|
||||
};
|
||||
},
|
||||
[actions.START_QUERY_VALIDATION]() {
|
||||
let newState = { ...state };
|
||||
const sqlEditor = { id: action.query.sqlEditorId };
|
||||
newState = alterInArr(newState, 'queryEditors', sqlEditor, {
|
||||
validationResult: {
|
||||
id: action.query.id,
|
||||
errors: [],
|
||||
completed: false,
|
||||
},
|
||||
});
|
||||
return newState;
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
validationResult: {
|
||||
id: action.query.id,
|
||||
errors: [],
|
||||
completed: false,
|
||||
},
|
||||
},
|
||||
action.query.sqlEditorId,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_VALIDATION_RETURNED]() {
|
||||
// If the server is very slow about answering us, we might get validation
|
||||
@@ -202,21 +234,29 @@ export default function sqlLabReducer(state = {}, action) {
|
||||
// We don't care about any but the most recent because validations are
|
||||
// only valid for the SQL text they correspond to -- once the SQL has
|
||||
// changed, the old validation doesn't tell us anything useful anymore.
|
||||
const qe = getFromArr(state.queryEditors, action.query.sqlEditorId);
|
||||
const qe = {
|
||||
...getFromArr(state.queryEditors, action.query.sqlEditorId),
|
||||
...(state.unsavedQueryEditor.id === action.query.sqlEditorId &&
|
||||
state.unsavedQueryEditor),
|
||||
};
|
||||
if (qe.validationResult.id !== action.query.id) {
|
||||
return state;
|
||||
}
|
||||
// Otherwise, persist the results on the queryEditor state
|
||||
let newState = { ...state };
|
||||
const sqlEditor = { id: action.query.sqlEditorId };
|
||||
newState = alterInArr(newState, 'queryEditors', sqlEditor, {
|
||||
validationResult: {
|
||||
id: action.query.id,
|
||||
errors: action.results,
|
||||
completed: true,
|
||||
},
|
||||
});
|
||||
return newState;
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
validationResult: {
|
||||
id: action.query.id,
|
||||
errors: action.results,
|
||||
completed: true,
|
||||
},
|
||||
},
|
||||
action.query.sqlEditorId,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_VALIDATION_FAILED]() {
|
||||
// If the server is very slow about answering us, we might get validation
|
||||
@@ -250,45 +290,52 @@ export default function sqlLabReducer(state = {}, action) {
|
||||
return newState;
|
||||
},
|
||||
[actions.COST_ESTIMATE_STARTED]() {
|
||||
let newState = { ...state };
|
||||
const sqlEditor = { id: action.query.sqlEditorId };
|
||||
newState = alterInArr(newState, 'queryEditors', sqlEditor, {
|
||||
queryCostEstimate: {
|
||||
completed: false,
|
||||
cost: null,
|
||||
error: null,
|
||||
return {
|
||||
...state,
|
||||
queryCostEstimates: {
|
||||
...state.queryCostEstimates,
|
||||
[action.query.sqlEditorId]: {
|
||||
completed: false,
|
||||
cost: null,
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
return newState;
|
||||
};
|
||||
},
|
||||
[actions.COST_ESTIMATE_RETURNED]() {
|
||||
let newState = { ...state };
|
||||
const sqlEditor = { id: action.query.sqlEditorId };
|
||||
newState = alterInArr(newState, 'queryEditors', sqlEditor, {
|
||||
queryCostEstimate: {
|
||||
completed: true,
|
||||
cost: action.json,
|
||||
error: null,
|
||||
return {
|
||||
...state,
|
||||
queryCostEstimates: {
|
||||
...state.queryCostEstimates,
|
||||
[action.query.sqlEditorId]: {
|
||||
completed: true,
|
||||
cost: action.json,
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
return newState;
|
||||
};
|
||||
},
|
||||
[actions.COST_ESTIMATE_FAILED]() {
|
||||
let newState = { ...state };
|
||||
const sqlEditor = { id: action.query.sqlEditorId };
|
||||
newState = alterInArr(newState, 'queryEditors', sqlEditor, {
|
||||
queryCostEstimate: {
|
||||
completed: false,
|
||||
cost: null,
|
||||
error: action.error,
|
||||
return {
|
||||
...state,
|
||||
queryCostEstimates: {
|
||||
...state.queryCostEstimates,
|
||||
[action.query.sqlEditorId]: {
|
||||
completed: false,
|
||||
cost: null,
|
||||
error: action.error,
|
||||
},
|
||||
},
|
||||
});
|
||||
return newState;
|
||||
};
|
||||
},
|
||||
[actions.START_QUERY]() {
|
||||
let newState = { ...state };
|
||||
if (action.query.sqlEditorId) {
|
||||
const qe = getFromArr(state.queryEditors, action.query.sqlEditorId);
|
||||
const qe = {
|
||||
...getFromArr(state.queryEditors, action.query.sqlEditorId),
|
||||
...(action.query.sqlEditorId === state.unsavedQueryEditor.id &&
|
||||
state.unsavedQueryEditor),
|
||||
};
|
||||
if (qe.latestQueryId && state.queries[qe.latestQueryId]) {
|
||||
const newResults = {
|
||||
...state.queries[qe.latestQueryId].results,
|
||||
@@ -303,10 +350,17 @@ export default function sqlLabReducer(state = {}, action) {
|
||||
newState.activeSouthPaneTab = action.query.id;
|
||||
}
|
||||
newState = addToObject(newState, 'queries', action.query);
|
||||
const sqlEditor = { id: action.query.sqlEditorId };
|
||||
return alterInArr(newState, 'queryEditors', sqlEditor, {
|
||||
latestQueryId: action.query.id,
|
||||
});
|
||||
|
||||
return {
|
||||
...newState,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
latestQueryId: action.query.id,
|
||||
},
|
||||
action.query.sqlEditorId,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.STOP_QUERY]() {
|
||||
return alterInObject(state, 'queries', action.query, {
|
||||
@@ -371,14 +425,41 @@ export default function sqlLabReducer(state = {}, action) {
|
||||
qeIds.indexOf(action.queryEditor?.id) > -1 &&
|
||||
state.tabHistory[state.tabHistory.length - 1] !== action.queryEditor.id
|
||||
) {
|
||||
const tabHistory = state.tabHistory.slice();
|
||||
tabHistory.push(action.queryEditor.id);
|
||||
return { ...state, tabHistory };
|
||||
const mergeUnsavedState = alterInArr(
|
||||
state,
|
||||
'queryEditors',
|
||||
state.unsavedQueryEditor,
|
||||
{
|
||||
...state.unsavedQueryEditor,
|
||||
},
|
||||
);
|
||||
return {
|
||||
...(action.queryEditor.id === state.unsavedQueryEditor.id
|
||||
? alterInObject(
|
||||
mergeUnsavedState,
|
||||
'queryEditors',
|
||||
action.queryEditor,
|
||||
{
|
||||
...action.queryEditor,
|
||||
...state.unsavedQueryEditor,
|
||||
},
|
||||
)
|
||||
: mergeUnsavedState),
|
||||
tabHistory: [...state.tabHistory, action.queryEditor.id],
|
||||
};
|
||||
}
|
||||
return state;
|
||||
},
|
||||
[actions.LOAD_QUERY_EDITOR]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
const mergeUnsavedState = alterInArr(
|
||||
state,
|
||||
'queryEditors',
|
||||
state.unsavedQueryEditor,
|
||||
{
|
||||
...state.unsavedQueryEditor,
|
||||
},
|
||||
);
|
||||
return alterInArr(mergeUnsavedState, 'queryEditors', action.queryEditor, {
|
||||
...action.queryEditor,
|
||||
});
|
||||
},
|
||||
@@ -441,70 +522,161 @@ export default function sqlLabReducer(state = {}, action) {
|
||||
return { ...state, queries };
|
||||
},
|
||||
[actions.QUERY_EDITOR_SETDB]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
dbId: action.dbId,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
dbId: action.dbId,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_SET_FUNCTION_NAMES]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
functionNames: action.functionNames,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
functionNames: action.functionNames,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_SET_SCHEMA]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
schema: action.schema,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
schema: action.schema,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_SET_SCHEMA_OPTIONS]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
schemaOptions: action.options,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
schemaOptions: action.options,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_SET_TABLE_OPTIONS]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
tableOptions: action.options,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
tableOptions: action.options,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_SET_TITLE]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
name: action.name,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
name: action.name,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_SET_SQL]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
sql: action.sql,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
sql: action.sql,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_SET_QUERY_LIMIT]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
queryLimit: action.queryLimit,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
queryLimit: action.queryLimit,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_SET_TEMPLATE_PARAMS]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
templateParams: action.templateParams,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
templateParams: action.templateParams,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_SET_SELECTED_TEXT]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
selectedText: action.sql,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
selectedText: action.sql,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_SET_AUTORUN]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
autorun: action.autorun,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
autorun: action.autorun,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_PERSIST_HEIGHT]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
northPercent: action.northPercent,
|
||||
southPercent: action.southPercent,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
northPercent: action.northPercent,
|
||||
southPercent: action.southPercent,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.QUERY_EDITOR_TOGGLE_LEFT_BAR]() {
|
||||
return alterInArr(state, 'queryEditors', action.queryEditor, {
|
||||
hideLeftBar: action.hideLeftBar,
|
||||
});
|
||||
return {
|
||||
...state,
|
||||
unsavedQueryEditor: alterUnsavedQueryEditorState(
|
||||
state,
|
||||
{
|
||||
hideLeftBar: action.hideLeftBar,
|
||||
},
|
||||
action.queryEditor.id,
|
||||
),
|
||||
};
|
||||
},
|
||||
[actions.SET_DATABASES]() {
|
||||
const databases = {};
|
||||
|
||||
Reference in New Issue
Block a user