Optimize reference update tracking by sourcing from direct modifications rather than state diff comparisons.

This commit is contained in:
arpitnath 2024-04-04 20:54:13 +05:30
parent 1c4c272c9f
commit d4e9f45ae2
6 changed files with 95 additions and 39 deletions

View file

@ -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);

View file

@ -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,
}); });

View file

@ -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);
} }

View file

@ -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();
}; };

View file

@ -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 } }));

View file

@ -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 });
} }