mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-24 09:28:31 +00:00
Optimize reference update tracking by sourcing from direct modifications rather than state diff comparisons.
This commit is contained in:
parent
1c4c272c9f
commit
d4e9f45ae2
6 changed files with 95 additions and 39 deletions
|
|
@ -243,10 +243,10 @@ export const resolveReferences = (query, validationSchema, customResolvers = {},
|
||||||
if (fxActive && (value.startsWith('#') || value.includes('table-'))) {
|
if (fxActive && (value.startsWith('#') || value.includes('table-'))) {
|
||||||
value = JSON.stringify(value);
|
value = JSON.stringify(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { toResolveReference, jsExpression, jsExpMatch } = inferJSExpAndReferences(value, lookupTable.hints);
|
const { toResolveReference, jsExpression, jsExpMatch } = inferJSExpAndReferences(value, lookupTable.hints);
|
||||||
const isComponentValue = toResolveReference?.startsWith('components.') || false; //!Notes: As we removed the updating of references on currentState changes, exposed variable of components are dynamic and cannot be controlled in any form, so we are resolving only components references with our legacy approach.
|
|
||||||
if (!isComponentValue && !jsExpMatch && toResolveReference && lookupTable.hints.has(toResolveReference)) {
|
// const isComponentValue = toResolveReference?.startsWith('components.') || false; //!Notes: As we removed the updating of references on currentState changes, exposed variable of components are dynamic and cannot be controlled in any form, so we are resolving only components references with our legacy approach.
|
||||||
|
if (!jsExpMatch && toResolveReference && lookupTable.hints.has(toResolveReference)) {
|
||||||
const idToLookUp = lookupTable.hints.get(toResolveReference);
|
const idToLookUp = lookupTable.hints.get(toResolveReference);
|
||||||
resolvedValue = lookupTable.resolvedRefs.get(idToLookUp);
|
resolvedValue = lookupTable.resolvedRefs.get(idToLookUp);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -326,47 +326,19 @@ const EditorComponent = (props) => {
|
||||||
flushComponentsToRender(updatedComponentIds);
|
flushComponentsToRender(updatedComponentIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lastUpdatedRef = useResolveStore((state) => state.lastUpdatedRefs, shallow);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const isEditorReady = useCurrentStateStore.getState().isEditorReady;
|
if (lastUpdatedRef.length > 0) {
|
||||||
|
|
||||||
if (!isEditorReady || !onAppLoadAndPageLoadEventsAreTriggered.current) return;
|
|
||||||
|
|
||||||
const diffState = diff(prevCurrentStateRef.current, currentState);
|
|
||||||
|
|
||||||
if (Object.keys(diffState).length > 0) {
|
|
||||||
const entitiesToTrack = ['queries', 'components', 'variables', 'page', 'constants'];
|
|
||||||
|
|
||||||
const entitiesChanged = Object.keys(diffState).filter((entity) => entitiesToTrack.includes(entity));
|
|
||||||
|
|
||||||
if (entitiesChanged.length === 0) return;
|
|
||||||
|
|
||||||
const diffObj = entitiesChanged.reduce((acc, entity) => {
|
|
||||||
acc[entity] = diffState[entity];
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const allPaths = entitiesChanged.reduce((acc, entity) => {
|
|
||||||
const paths = Object.keys(diffObj[entity]).map((key) => {
|
|
||||||
return generatePath(diffObj[entity], key);
|
|
||||||
});
|
|
||||||
|
|
||||||
acc[entity] = paths.map((path) => `${entity}.${path}`);
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const currentStatePaths = Object.values(allPaths).flat();
|
|
||||||
|
|
||||||
const currentComponents = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components || {};
|
const currentComponents = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components || {};
|
||||||
const componentIdsWithReferences = findComponentsWithReferences(currentComponents, currentStatePaths);
|
const componentIdsWithReferences = findComponentsWithReferences(currentComponents, lastUpdatedRef);
|
||||||
|
|
||||||
if (componentIdsWithReferences.length > 0) {
|
if (componentIdsWithReferences.length > 0) {
|
||||||
batchUpdateComponents(componentIdsWithReferences);
|
batchUpdateComponents(componentIdsWithReferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
prevCurrentStateRef.current = JSON.parse(JSON.stringify(currentState));
|
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [JSON.stringify(currentState)]);
|
}, [lastUpdatedRef]);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -1175,7 +1147,6 @@ const EditorComponent = (props) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
useResolveStore.getState().actions.addEntitiesToMap(componentEntityArray);
|
useResolveStore.getState().actions.addEntitiesToMap(componentEntityArray);
|
||||||
|
|
||||||
useResolveStore.getState().actions.addAppSuggestions({
|
useResolveStore.getState().actions.addAppSuggestions({
|
||||||
components: newComponentsExposedData,
|
components: newComponentsExposedData,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import { useAppDataStore } from '@/_stores/appDataStore';
|
||||||
import { useEditorStore } from '@/_stores/editorStore';
|
import { useEditorStore } from '@/_stores/editorStore';
|
||||||
import { useGridStore } from '@/_stores/gridStore';
|
import { useGridStore } from '@/_stores/gridStore';
|
||||||
import { useResolveStore } from '@/_stores/resolverStore';
|
import { useResolveStore } from '@/_stores/resolverStore';
|
||||||
|
import { handleLowPriorityWork } from './editorHelpers';
|
||||||
|
|
||||||
const ERROR_TYPES = Object.freeze({
|
const ERROR_TYPES = Object.freeze({
|
||||||
ReferenceError: 'ReferenceError',
|
ReferenceError: 'ReferenceError',
|
||||||
|
|
@ -115,11 +116,31 @@ export function onComponentOptionChanged(component, option_name, value) {
|
||||||
let componentData = components[componentName] || {};
|
let componentData = components[componentName] || {};
|
||||||
componentData[option_name] = value;
|
componentData[option_name] = value;
|
||||||
|
|
||||||
|
const path = option_name ? `components.${componentName}.${option_name}` : null;
|
||||||
|
|
||||||
if (isEditorReady) {
|
if (isEditorReady) {
|
||||||
// Always update the current state if editor is ready
|
// Always update the current state if editor is ready
|
||||||
useCurrentStateStore.getState().actions.setCurrentState({
|
useCurrentStateStore.getState().actions.setCurrentState({
|
||||||
components: { ...components, [componentName]: componentData },
|
components: { ...components, [componentName]: componentData },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!_.isEmpty(useResolveStore.getState().lookupTable?.resolvedRefs) && path) {
|
||||||
|
const lookUpTable = useResolveStore.getState().lookupTable;
|
||||||
|
|
||||||
|
const existingRef = lookUpTable.resolvedRefs?.get(lookUpTable.hints?.get(path));
|
||||||
|
|
||||||
|
if (typeof existingRef === 'function') return;
|
||||||
|
|
||||||
|
const shouldUpdateRef = existingRef !== componentData[option_name];
|
||||||
|
|
||||||
|
if (shouldUpdateRef) {
|
||||||
|
handleLowPriorityWork(() => {
|
||||||
|
useResolveStore
|
||||||
|
.getState()
|
||||||
|
.actions.updateResolvedRefsOfHints([{ hint: path, newRef: componentData[option_name] }]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Update the duplicate state if editor is not ready
|
// Update the duplicate state if editor is not ready
|
||||||
duplicateCurrentState = { ...components, [componentName]: componentData };
|
duplicateCurrentState = { ...components, [componentName]: componentData };
|
||||||
|
|
@ -1091,6 +1112,14 @@ export function runQuery(
|
||||||
},
|
},
|
||||||
errors: {},
|
errors: {},
|
||||||
});
|
});
|
||||||
|
useResolveStore.getState().actions.addAppSuggestions({
|
||||||
|
queries: {
|
||||||
|
[queryName]: {
|
||||||
|
data: [],
|
||||||
|
isLoading: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let queryExecutionPromise = null;
|
let queryExecutionPromise = null;
|
||||||
if (query.kind === 'runjs') {
|
if (query.kind === 'runjs') {
|
||||||
|
|
@ -1267,9 +1296,15 @@ export function runQuery(
|
||||||
queries: {
|
queries: {
|
||||||
[queryName]: {
|
[queryName]: {
|
||||||
data: finalData,
|
data: finalData,
|
||||||
|
isLoading: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const basePath = `queries.${queryName}`;
|
||||||
|
|
||||||
|
useResolveStore.getState().actions.updateLastUpdatedRefs([`${basePath}.data`, `${basePath}.isLoading`]);
|
||||||
|
|
||||||
resolve({ status: 'ok', data: finalData });
|
resolve({ status: 'ok', data: finalData });
|
||||||
onEvent(_self, 'onDataQuerySuccess', queryEvents, mode);
|
onEvent(_self, 'onDataQuerySuccess', queryEvents, mode);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { create } from './utils';
|
import { create } from './utils';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import { useResolveStore } from './resolverStore';
|
||||||
const STORE_NAME = 'Editor';
|
const STORE_NAME = 'Editor';
|
||||||
|
|
||||||
export const EMPTY_ARRAY = [];
|
export const EMPTY_ARRAY = [];
|
||||||
|
|
@ -117,4 +118,5 @@ export const flushComponentsToRender = (componentIds = []) => {
|
||||||
if (!componentIds.length) return;
|
if (!componentIds.length) return;
|
||||||
|
|
||||||
useEditorStore.getState().actions.flushComponentsNeedsUpdateOnNextRender(componentIds);
|
useEditorStore.getState().actions.flushComponentsNeedsUpdateOnNextRender(componentIds);
|
||||||
|
useResolveStore.getState().actions.getLastUpdatedRefs();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ const initialState = {
|
||||||
hints: {},
|
hints: {},
|
||||||
resolvedRefs: {},
|
resolvedRefs: {},
|
||||||
},
|
},
|
||||||
|
lastUpdatedRefs: [],
|
||||||
referenceMapper: new ReferencesBiMap(),
|
referenceMapper: new ReferencesBiMap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -78,6 +78,16 @@ export const useResolveStore = create(
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
flushLastUpdatedRefs: () => {
|
||||||
|
set(() => ({ lastUpdatedRefs: [] }));
|
||||||
|
},
|
||||||
|
getLastUpdatedRefs: () => {
|
||||||
|
return get().lastUpdatedRefs;
|
||||||
|
},
|
||||||
|
//for queries references used in component definitons
|
||||||
|
updateLastUpdatedRefs: (updatedRefs) => {
|
||||||
|
set(() => ({ lastUpdatedRefs: updatedRefs }));
|
||||||
|
},
|
||||||
addAppSuggestions: (partialRefState) => {
|
addAppSuggestions: (partialRefState) => {
|
||||||
if (Object.keys(partialRefState).length === 0) return;
|
if (Object.keys(partialRefState).length === 0) return;
|
||||||
|
|
||||||
|
|
@ -86,6 +96,8 @@ export const useResolveStore = create(
|
||||||
const lookupHintsMap = new Map([...get().lookupTable.hints]);
|
const lookupHintsMap = new Map([...get().lookupTable.hints]);
|
||||||
const lookupResolvedRefs = new Map([...get().lookupTable.resolvedRefs]);
|
const lookupResolvedRefs = new Map([...get().lookupTable.resolvedRefs]);
|
||||||
|
|
||||||
|
const newUpdatedrefs = [];
|
||||||
|
|
||||||
hintsMap.forEach((value, key) => {
|
hintsMap.forEach((value, key) => {
|
||||||
const alreadyExists = lookupHintsMap.has(key);
|
const alreadyExists = lookupHintsMap.has(key);
|
||||||
|
|
||||||
|
|
@ -97,6 +109,7 @@ export const useResolveStore = create(
|
||||||
|
|
||||||
resolvedRefs.delete(value);
|
resolvedRefs.delete(value);
|
||||||
resolvedRefs.set(existingLookupId, newResolvedRef);
|
resolvedRefs.set(existingLookupId, newResolvedRef);
|
||||||
|
newUpdatedrefs.push(key);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -118,6 +131,7 @@ export const useResolveStore = create(
|
||||||
hints: lookupHintsMap,
|
hints: lookupHintsMap,
|
||||||
resolvedRefs: lookupResolvedRefs,
|
resolvedRefs: lookupResolvedRefs,
|
||||||
},
|
},
|
||||||
|
lastUpdatedRefs: newUpdatedrefs,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -158,6 +172,35 @@ export const useResolveStore = create(
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateResolvedRefsOfHints: (resolvedRefs = []) => {
|
||||||
|
const lookupResolvedRefs = new Map([...get().lookupTable.resolvedRefs]);
|
||||||
|
const hintsMap = new Map([...get().lookupTable.hints]);
|
||||||
|
|
||||||
|
const updatedList = [];
|
||||||
|
|
||||||
|
resolvedRefs.forEach((ref) => {
|
||||||
|
if (!ref.hint || !ref.newRef || !hintsMap.has(ref.hint)) return;
|
||||||
|
|
||||||
|
const refId = hintsMap.get(ref.hint);
|
||||||
|
const currentRef = lookupResolvedRefs.get(refId);
|
||||||
|
|
||||||
|
if (currentRef !== ref.newRef) {
|
||||||
|
lookupResolvedRefs.set(refId, ref.newRef);
|
||||||
|
updatedList.push(ref.hint);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updatedList.length > 0) {
|
||||||
|
set(() => ({
|
||||||
|
lookupTable: {
|
||||||
|
...get().lookupTable,
|
||||||
|
resolvedRefs: lookupResolvedRefs,
|
||||||
|
},
|
||||||
|
lastUpdatedRefs: updatedList,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
updateJSHints: () => {
|
updateJSHints: () => {
|
||||||
const hints = createJavaScriptSuggestions();
|
const hints = createJavaScriptSuggestions();
|
||||||
set(() => ({ suggestions: { ...get().suggestions, jsHints: hints } }));
|
set(() => ({ suggestions: { ...get().suggestions, jsHints: hints } }));
|
||||||
|
|
|
||||||
|
|
@ -465,7 +465,12 @@ export function createReferencesLookup(refState, forQueryParams = false, initalL
|
||||||
}
|
}
|
||||||
if (_type === 'Array') {
|
if (_type === 'Array') {
|
||||||
map.set(newPath, { type: _type });
|
map.set(newPath, { type: _type });
|
||||||
buildMap(value, newPath);
|
|
||||||
|
if (path.startsWith('queries') && key === 'data' && value.length > 2) {
|
||||||
|
// do nothing
|
||||||
|
} else {
|
||||||
|
buildMap(value, newPath);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
map.set(newPath, { type: _type });
|
map.set(newPath, { type: _type });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue