handle page swich

- on page switch, reset resolver stores for new references and hints to get updated
- on current state initated on page switch should not out setting up updating resolver store to micro task
- refactored and optimised the core functions to find references associated as graph
This commit is contained in:
arpitnath 2024-04-06 17:05:19 +05:30
parent 59de1703ac
commit a4a033cc6f
7 changed files with 171 additions and 143 deletions

View file

@ -200,7 +200,6 @@ const resolveMultiDynamicReferences = (code, lookupTable) => {
resolvedValue = resolvedValue.replace(variable, res);
} else {
const currentState = useCurrentStateStore.getState();
const [resolvedCode] = resolveCode(variableToResolve, {}, true, [], true);
resolvedValue = resolvedCode;
@ -241,7 +240,10 @@ export const resolveReferences = (query, validationSchema, customResolvers = {})
if (value.startsWith('#') || value.includes('table-')) {
value = JSON.stringify(value);
}
const { toResolveReference, jsExpression, jsExpMatch } = inferJSExpAndReferences(value, lookupTable.hints);
const { toResolveReference, jsExpression, jsExpMatch } =
lookupTable.hints || lookupTable.hints.has
? inferJSExpAndReferences(value, lookupTable.hints)
: { toResolveReference: null, jsExpression: null, jsExpMatch: null };
if (!jsExpMatch && toResolveReference && lookupTable.hints.has(toResolveReference)) {
const idToLookUp = lookupTable.hints.get(toResolveReference);
@ -307,7 +309,7 @@ const inferJSExpAndReferences = (code, hintsMap) => {
const potentialReference = referenceChain ? referenceChain + '.' + segment : segment;
// Check if the potential reference exists in hintsMap
if (hintsMap.has(potentialReference)) {
if (hintsMap.has && hintsMap.has(potentialReference)) {
// If it does, update the referenceChain
referenceChain = potentialReference;
} else {

View file

@ -632,8 +632,18 @@ const EditorComponent = (props) => {
props.switchDarkMode(newMode);
};
const handleEvent = React.useCallback((eventName, event, options) => {
return onEvent(getEditorRef(), eventName, event, options, 'edit');
const handleEvent = React.useCallback((eventName, events, options) => {
const latestEvents = useAppDataStore.getState().events;
const filteredEvents = latestEvents.filter((event) => {
const foundEvent = events.find((e) => e.id === event.id);
return foundEvent && foundEvent.name === eventName;
});
try {
return onEvent(getEditorRef(), eventName, filteredEvents, options, 'edit');
} catch (error) {
console.error(error);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@ -765,117 +775,9 @@ const EditorComponent = (props) => {
await fetchDataSources(data.editing_version?.id),
await fetchDataQueries(data.editing_version?.id, true, true),
])
.then(() => {
useCurrentStateStore.getState().actions.setEditorReady(true);
const currentPageId = useEditorStore.getState().currentPageId;
const currentComponents = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components;
const referenceManager = useResolveStore.getState().referenceMapper;
const newComponents = Object.keys(currentComponents).map((componentId) => {
const component = currentComponents[componentId];
if (!referenceManager.get(componentId)) {
return {
id: componentId,
name: component.component.name,
};
}
});
useResolveStore.getState().actions.addEntitiesToMap(newComponents);
})
.then(() => {
const currentPageId = useEditorStore.getState().currentPageId;
const currentComponents = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components;
let dataQueries = JSON.parse(JSON.stringify(useDataQueriesStore.getState().dataQueries));
let allEvents = JSON.parse(JSON.stringify(useAppDataStore.getState().events));
const entityReferencesInComponentDefinitions = findAllEntityReferences(currentComponents, [])?.filter(
(entity) => entity && isValidUUID(entity)
);
const entityReferencesInQueryOptions = findAllEntityReferences(dataQueries, [])?.filter(
(entity) => entity && isValidUUID(entity)
);
const entityReferencesInEvents = findAllEntityReferences(allEvents, [])?.filter(
(entity) => entity && isValidUUID(entity)
);
const manager = useResolveStore.getState().referenceMapper;
if (
Array.isArray(entityReferencesInComponentDefinitions) &&
entityReferencesInComponentDefinitions?.length > 0
) {
let newComponentDefinition = JSON.parse(JSON.stringify(currentComponents));
entityReferencesInComponentDefinitions.forEach((entity) => {
const entityrefExists = manager.has(entity);
if (entityrefExists) {
const value = manager.get(entity);
newComponentDefinition = dfs(newComponentDefinition, entity, value);
}
});
const newAppDefinition = produce(appJson, (draft) => {
draft.pages[homePageId].components = newComponentDefinition;
});
updateEditorState({
isUpdatingEditorStateInProcess: false,
appDefinition: newAppDefinition,
});
}
if (Array.isArray(entityReferencesInQueryOptions) && entityReferencesInQueryOptions?.length > 0) {
let newQueryOptions = {};
dataQueries?.forEach((query) => {
newQueryOptions[query.id] = query.options;
``;
});
entityReferencesInQueryOptions.forEach((entity) => {
const entityrefExists = manager.has(entity);
if (entityrefExists) {
const value = manager.get(entity);
newQueryOptions = dfs(newQueryOptions, entity, value);
}
});
dataQueries = dataQueries.map((query) => {
const queryId = query.id;
const dqOptions = newQueryOptions[queryId];
return {
...query,
options: dqOptions,
};
});
useDataQueriesStore.getState().actions.setDataQueries(dataQueries, 'mappingUpdate');
}
if (Array.isArray(entityReferencesInEvents) && entityReferencesInEvents?.length > 0) {
let newEvents = JSON.parse(JSON.stringify(allEvents));
entityReferencesInEvents.forEach((entity) => {
const entityrefExists = manager.has(entity);
if (entityrefExists) {
const value = manager.get(entity);
newEvents = dfs(newEvents, entity, value);
}
});
updateState({
events: newEvents,
});
}
.then(async () => {
await onEditorLoad(appJson, homePageId);
updateEntityReferences(appJson, homePageId);
})
.finally(async () => {
const currentPageEvents = data.events.filter(
@ -1478,6 +1380,117 @@ const EditorComponent = (props) => {
}
};
const onEditorLoad = (appJson, pageId, isPageSwitch = false) => {
useCurrentStateStore.getState().actions.setEditorReady(true);
const currentComponents = appJson?.pages?.[pageId]?.components;
const referenceManager = useResolveStore.getState().referenceMapper;
const newComponents = Object.keys(currentComponents).map((componentId) => {
const component = currentComponents[componentId];
if (isPageSwitch || !referenceManager.get(componentId)) {
return {
id: componentId,
name: component.component.name,
};
}
});
useResolveStore.getState().actions.addEntitiesToMap(newComponents);
};
const updateEntityReferences = (appJson, pageId) => {
const currentComponents = appJson?.pages?.[pageId]?.components;
let dataQueries = JSON.parse(JSON.stringify(useDataQueriesStore.getState().dataQueries));
let allEvents = JSON.parse(JSON.stringify(useAppDataStore.getState().events));
const entityReferencesInComponentDefinitions = findAllEntityReferences(currentComponents, [])?.filter(
(entity) => entity && isValidUUID(entity)
);
const entityReferencesInQueryOptions = findAllEntityReferences(dataQueries, [])?.filter(
(entity) => entity && isValidUUID(entity)
);
const entityReferencesInEvents = findAllEntityReferences(allEvents, [])?.filter(
(entity) => entity && isValidUUID(entity)
);
const manager = useResolveStore.getState().referenceMapper;
if (Array.isArray(entityReferencesInComponentDefinitions) && entityReferencesInComponentDefinitions?.length > 0) {
let newComponentDefinition = JSON.parse(JSON.stringify(currentComponents));
entityReferencesInComponentDefinitions.forEach((entity) => {
const entityrefExists = manager.has(entity);
if (entityrefExists) {
const value = manager.get(entity);
newComponentDefinition = dfs(newComponentDefinition, entity, value);
}
});
const newAppDefinition = produce(appJson, (draft) => {
draft.pages[pageId].components = newComponentDefinition;
});
handleLowPriorityWork(() => {
updateEditorState({
isUpdatingEditorStateInProcess: false,
appDefinition: newAppDefinition,
});
});
}
if (Array.isArray(entityReferencesInQueryOptions) && entityReferencesInQueryOptions?.length > 0) {
let newQueryOptions = {};
dataQueries?.forEach((query) => {
newQueryOptions[query.id] = query.options;
``;
});
entityReferencesInQueryOptions.forEach((entity) => {
const entityrefExists = manager.has(entity);
if (entityrefExists) {
const value = manager.get(entity);
newQueryOptions = dfs(newQueryOptions, entity, value);
}
});
dataQueries = dataQueries.map((query) => {
const queryId = query.id;
const dqOptions = newQueryOptions[queryId];
return {
...query,
options: dqOptions,
};
});
useDataQueriesStore.getState().actions.setDataQueries(dataQueries, 'mappingUpdate');
}
if (Array.isArray(entityReferencesInEvents) && entityReferencesInEvents?.length > 0) {
let newEvents = JSON.parse(JSON.stringify(allEvents));
entityReferencesInEvents.forEach((entity) => {
const entityrefExists = manager.has(entity);
if (entityrefExists) {
const value = manager.get(entity);
newEvents = dfs(newEvents, entity, value);
}
});
updateState({
events: newEvents,
});
}
};
const removeComponents = () => {
const selectedComponents = useEditorStore.getState()?.selectedComponents;
if (!isVersionReleased && selectedComponents?.length > 1) {
@ -1592,7 +1605,9 @@ const EditorComponent = (props) => {
});
};
const switchPage = (pageId, queryParams = []) => {
const switchPage = async (pageId, queryParams = []) => {
useCurrentStateStore.getState().actions.setEditorReady(false);
useResolveStore.getState().actions.resetStore();
// This are fetched from store to handle runQueriesOnAppLoad
const currentPageId = useEditorStore.getState().currentPageId;
const appDefinition = useEditorStore.getState().appDefinition;
@ -1619,7 +1634,13 @@ const EditorComponent = (props) => {
...currentState.globals,
urlparams: JSON.parse(JSON.stringify(queryString.parse(queryParamsString))),
};
useCurrentStateStore.getState().actions.setCurrentState({ globals, page });
useResolveStore.getState().actions.pageSwitched(true);
await onEditorLoad(appDefinition, pageId, true);
updateEntityReferences(appDefinition, pageId);
useResolveStore.getState().actions.updateJSHints();
setCurrentPageId(pageId);

View file

@ -174,7 +174,11 @@ export function findComponentsWithReferences(components, changedCurrentState) {
return componentIdsWithReferences;
}
export function handleLowPriorityWork(callback, timeout = null) {
export function handleLowPriorityWork(callback, timeout = null, immediate = false) {
if (immediate) {
callback();
}
const options = timeout ? { timeout } : {};
window.requestIdleCallback(callback, options);
}

View file

@ -3,15 +3,8 @@ import { useRef, useEffect } from 'react';
function useRenderCount(componentName) {
const renderCountRef = useRef(0);
useEffect(() => {
return () => {
console.log(`--Component ${componentName} rendered unmounting ${renderCountRef.current} times.`);
};
}, []);
renderCountRef.current++;
console.log(`CountingRender- Component ${componentName} rendered ${renderCountRef.current} times.`);
return renderCountRef.current;
}

View file

@ -46,7 +46,6 @@ export const useAppDataStore = create(
updateEditingVersion: (version) => set(() => ({ editingVersion: version })),
updateApps: (apps) => set(() => ({ apps: apps })),
updateState: (state) => set((prev) => ({ ...prev, ...state })),
updateAppDefinitionDiff: (appDefinitionDiff) => set(() => ({ appDefinitionDiff: appDefinitionDiff })),
updateAppVersion: (appId, versionId, pageId, appDefinitionDiff, isUserSwitchedVersion = false) => {
return new Promise((resolve, reject) => {

View file

@ -1,10 +1,7 @@
import { shallow } from 'zustand/shallow';
import { create, zustandDevTools } from './utils';
import _, { debounce, merge, omit } from 'lodash';
import _, { omit } from 'lodash';
import { useResolveStore } from './resolverStore';
// eslint-disable-next-line import/no-unresolved
import { diff } from 'deep-object-diff';
import { useEditorStore } from './editorStore';
import { handleLowPriorityWork } from '@/_helpers/editorHelpers';
const initialState = {
@ -88,19 +85,26 @@ useCurrentStateStore.subscribe((state) => {
const isStoreIntialized = useResolveStore.getState().storeReady;
if (!isStoreIntialized) {
handleLowPriorityWork(() => {
useResolveStore.getState().actions.updateAppSuggestions({
queries: state.queries,
components: state.components,
globals: state.globals,
page: state.page,
variables: state.variables,
client: state.client,
server: state.server,
constants: state.constants,
});
});
console.log('Resolver store initialized with current state.');
const isPageSwitched = useResolveStore.getState().isPageSwitched;
handleLowPriorityWork(
() => {
useResolveStore.getState().actions.updateAppSuggestions({
queries: state.queries,
components: state.components,
globals: state.globals,
page: state.page,
variables: state.variables,
client: state.client,
server: state.server,
constants: state.constants,
});
useResolveStore.getState().actions.pageSwitched(false);
},
null,
isPageSwitched
);
return useResolveStore.getState().actions.updateStoreState({ storeReady: true });
}
}, shallow);

View file

@ -60,6 +60,7 @@ const initialState = {
},
lastUpdatedRefs: [],
referenceMapper: new ReferencesBiMap(),
isPageSwitched: false,
};
export const useResolveStore = create(
@ -69,6 +70,10 @@ export const useResolveStore = create(
updateStoreState: (state) => {
set(() => ({ ...state, storeReady: true }));
},
resetStore: () => {
set(() => initialState);
},
pageSwitched: (bool) => set(() => ({ isPageSwitched: bool })),
updateAppSuggestions: (refState) => {
const { suggestionList, hintsMap, resolvedRefs } = createReferencesLookup(refState, false, true);