fixes: component -extra re-rendering issue on current state changes.

This commit is contained in:
arpitnath 2024-04-03 11:45:24 +05:30
parent 858dff3606
commit 9b88e9f8b2
7 changed files with 148 additions and 136 deletions

View file

@ -128,8 +128,8 @@ const BoxUI = (props) => {
>
<ControlledComponentToRender
componentName={component.component}
onComponentClick={onComponentClick}
onComponentOptionChanged={onComponentOptionChanged}
// onComponentClick={onComponentClick}
// onComponentOptionChanged={onComponentOptionChanged}
onEvent={onEvent}
id={id}
paramUpdated={paramUpdated}

View file

@ -283,7 +283,7 @@ const EditorComponent = (props) => {
if (mounted && didAppDefinitionChanged && currentPageId) {
const components = appDefinition?.pages[currentPageId]?.components || {};
computeComponentState(components);
// computeComponentState(components);
if (appDiffOptions?.skipAutoSave === true || appDiffOptions?.entityReferenceUpdated === true) return;
@ -294,20 +294,61 @@ const EditorComponent = (props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify({ appDefinition, currentPageId, dataQueries })]);
const currentStateDiff = useEditorStore.getState().currentStateDiff;
useEffect(() => {
const isEditorReady = useCurrentStateStore.getState().isEditorReady;
const prevCurrentStateRef = useRef(currentState);
function generatePath(obj, targetKey, currentPath = '') {
for (const key in obj) {
const newPath = currentPath ? currentPath + '.' + key : key;
if (key === targetKey) {
return newPath;
}
if (typeof obj[key] === 'object' && obj[key] !== null) {
const result = generatePath(obj[key], targetKey, newPath);
if (result) {
return result;
}
}
}
return null;
}
useEffect(() => {
const diffState = diff(prevCurrentStateRef.current, currentState);
if (Object.keys(diffState).length > 0) {
const entitiesToTrack = ['queries', 'components', 'variables', 'page', 'constants', 'layout'];
const entitiesChanged = Object.keys(diffState).filter((entity) => entitiesToTrack.includes(entity));
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();
if (isEditorReady && currentStateDiff?.length > 0) {
const currentComponents = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components || {};
const componentIdsWithReferences = findComponentsWithReferences(currentComponents, currentStateDiff);
const componentIdsWithReferences = findComponentsWithReferences(currentComponents, currentStatePaths);
if (componentIdsWithReferences.length > 0) {
updateComponentsNeedsUpdateOnNextRender(componentIdsWithReferences);
}
prevCurrentStateRef.current = currentState;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(currentStateDiff)]);
}, [JSON.stringify(currentState)]);
useEffect(
() => {
@ -345,14 +386,6 @@ const EditorComponent = (props) => {
}
}, [currentLayout, mounted]);
const handleYmapEventUpdates = () => {
props.ymap?.set('eventHandlersUpdated', {
currentVersionId: currentVersionId,
currentSessionId: currentSessionId,
update: true,
});
};
const handleMessage = (event) => {
const { data } = event;
@ -743,13 +776,12 @@ const EditorComponent = (props) => {
useCurrentStateStore.getState().actions.setCurrentState({
page: currentpageData,
});
updateEditorState({
isLoading: false,
appDefinition: appJson,
isUpdatingEditorStateInProcess: false,
});
}),
updateEditorState({
isLoading: false,
appDefinition: appJson,
isUpdatingEditorStateInProcess: false,
});
updateState({ components: appJson.pages[homePageId]?.components });
@ -889,6 +921,8 @@ const EditorComponent = (props) => {
events: newEvents,
});
}
computeComponentState(currentComponents);
})
.finally(async () => {
const currentPageEvents = data.events.filter(

View file

@ -415,7 +415,6 @@ export const SubContainer = ({
if (didDrop && !parent) {
return;
}
console.log('---arpit::: drop --- on subcontainer', { item, isOver, isOverCurrent, didDrop });
if (item.component.component === 'PDF' && !isPDFSupported()) {
toast.error(

View file

@ -73,79 +73,85 @@ const debouncedChange = _.debounce(() => {
}, 100);
export function onComponentOptionsChanged(component, options) {
const componentName = component.name;
const { isEditorReady } = getCurrentState();
if (isEditorReady) {
if (duplicateCurrentState !== null) {
duplicateCurrentState = null;
}
if (!isEditorReady) return Promise.resolve();
const components = getCurrentState().components;
let componentData = components[componentName];
componentData = componentData || {};
const componentName = component.name;
for (const option of options) {
componentData[option[0]] = option[1];
}
// if (duplicateCurrentState !== null) {
// duplicateCurrentState = null;
// }
useCurrentStateStore.getState().actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
} else {
const components = duplicateCurrentState === null ? getCurrentState().components : duplicateCurrentState;
let componentData = components[componentName];
componentData = componentData || {};
const components = getCurrentState().components;
let componentData = components[componentName];
componentData = componentData || {};
for (const option of options) {
componentData[option[0]] = option[1];
}
duplicateCurrentState = { ...components, [componentName]: componentData };
debouncedChange();
for (const option of options) {
componentData[option[0]] = option[1];
}
useCurrentStateStore.getState().actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
return Promise.resolve();
}
// export function onComponentOptionChanged(component, option_name, value) {
// const componentName = component.name;
// const { isEditorReady } = getCurrentState();
// if (isEditorReady) {
// if (duplicateCurrentState !== null) {
// duplicateCurrentState = null;
// }
// const components = getCurrentState().components;
// let componentData = components[componentName];
// componentData = componentData || {};
// componentData[option_name] = value;
// if (option_name !== 'id') {
// useCurrentStateStore.getState().actions.setCurrentState({
// components: { ...components, [componentName]: componentData },
// });
// } else if (!componentData?.id) {
// useCurrentStateStore.getState().actions.setCurrentState({
// components: { ...components, [componentName]: componentData },
// });
// }
// useCurrentStateStore.getState().actions.setCurrentState({
// components: { ...components, [componentName]: componentData },
// });
// } else {
// const components = duplicateCurrentState === null ? getCurrentState().components : duplicateCurrentState;
// let componentData = components[componentName];
// componentData = componentData || {};
// componentData[option_name] = value;
// duplicateCurrentState = { ...components, [componentName]: componentData };
// if (option_name !== 'id') {
// debouncedChange();
// } else if (!componentData?.id) {
// debouncedChange();
// }
// }
// return Promise.resolve();
// }
export function onComponentOptionChanged(component, option_name, value) {
const { isEditorReady, components: currentComponents } = getCurrentState();
if (!isEditorReady) return Promise.resolve();
const componentName = component.name;
const { isEditorReady } = getCurrentState();
if (isEditorReady) {
if (duplicateCurrentState !== null) {
duplicateCurrentState = null;
}
const components = getCurrentState().components;
let componentData = components[componentName];
componentData = componentData || {};
componentData[option_name] = value;
const components = duplicateCurrentState === null ? currentComponents : duplicateCurrentState;
let componentData = components[componentName] || {};
componentData[option_name] = value;
if (option_name !== 'id') {
useCurrentStateStore.getState().actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
} else if (!componentData?.id) {
useCurrentStateStore.getState().actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
}
useCurrentStateStore.getState().actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
} else {
const components = duplicateCurrentState === null ? getCurrentState().components : duplicateCurrentState;
let componentData = components[componentName];
componentData = componentData || {};
componentData[option_name] = value;
duplicateCurrentState = { ...components, [componentName]: componentData };
if (option_name !== 'id') {
debouncedChange();
} else if (!componentData?.id) {
debouncedChange();
}
}
useCurrentStateStore.getState().actions.setCurrentState({
components: { ...components, [componentName]: componentData },
});
return Promise.resolve();
}
@ -1379,11 +1385,14 @@ export function computeComponentState(components = {}) {
}
});
useCurrentStateStore.getState().actions.setCurrentState({
components: {
...componentState,
useCurrentStateStore.getState().actions.setCurrentState(
{
components: {
...componentState,
},
},
});
'componentStateComputed'
);
return new Promise((resolve) => {
useEditorStore.getState().actions.updateEditorState({

View file

@ -139,9 +139,13 @@ function findReferenceInComponent(node, changedCurrentState) {
const value = node[key];
if (typeof value === 'string' && value.includes('{{') && value.includes('}}')) {
// Check if the referenced entity is in the state
if (changedCurrentState.some((state) => value.includes(state))) {
return true;
}
// check if the current node's value has the reference
const hasFound = changedCurrentState.some((state) => {
return value.includes(state);
});
return hasFound;
} else if (typeof value === 'object') {
const found = findReferenceInComponent(value, changedCurrentState);

View file

@ -1,6 +1,6 @@
import { shallow } from 'zustand/shallow';
import { create, zustandDevTools } from './utils';
import _, { omit } from 'lodash';
import _, { debounce, merge, omit } from 'lodash';
import { useResolveStore } from './resolverStore';
// eslint-disable-next-line import/no-unresolved
import { diff } from 'deep-object-diff';
@ -49,46 +49,10 @@ export const useCurrentStateStore = create(
(set, get) => ({
...initialState,
actions: {
setCurrentState: (currentState) => {
const currentStateEntites = Object.keys(currentState);
setCurrentState: (currentState, from = null) => {
const newState = merge({}, get(), currentState);
const existingStateOfEntities = currentStateEntites.reduce((acc, entity) => {
acc[entity] = get()[entity];
return acc;
}, {});
const diffState = diff(existingStateOfEntities, currentState);
if (_.isEmpty(diffState)) return;
set({ ...currentState }, false, { type: 'SET_CURRENT_STATE', currentState });
//need to track only queries, components, variables, page, constants, layout
// from the diff, if any of these entities are changed, we need to update the store
if (get().isEditorReady) {
const entitiesToTrack = ['queries', 'components', 'variables', 'page', 'constants', 'layout'];
const entitiesChanged = Object.keys(diffState).filter((entity) => entitiesToTrack.includes(entity));
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}`).join(',');
return acc;
}, {});
const currentStatePaths = Object.values(allPaths);
useEditorStore.getState().actions.updateCurrentStateDiff(currentStatePaths);
}
set({ ...newState });
},
setErrors: (error) => {
set({ errors: { ...get().errors, ...error } }, false, { type: 'SET_ERRORS', error });
@ -100,9 +64,9 @@ export const useCurrentStateStore = create(
)
);
export const useCurrentState = () =>
export const useCurrentState = () => {
// Omitting 'actions' here because we don't want to expose it to user
useCurrentStateStore((state) => {
const currentState = useCurrentStateStore((state) => {
return {
queries: state.queries,
components: state.components,
@ -118,6 +82,9 @@ export const useCurrentState = () =>
};
}, shallow);
return JSON.parse(JSON.stringify(currentState));
};
useCurrentStateStore.subscribe((state) => {
const isEditorReady = state.isEditorReady;

View file

@ -36,7 +36,6 @@ const initialState = {
queryConfirmationList: [],
currentPageId: null,
currentSessionId: uuid(),
currentStateDiff: [],
componentsNeedsUpdateOnNextRender: [],
};
@ -63,7 +62,7 @@ export const useEditorStore = create(
},
setIsEditorActive: (isEditorActive) => set(() => ({ isEditorActive })),
updateEditorState: (state) => set((prev) => ({ ...prev, ...state })),
updateCurrentStateDiff: (currentStateDiff) => set(() => ({ currentStateDiff })),
updateComponentsNeedsUpdateOnNextRender: (componentsNeedsUpdateOnNextRender) =>
set(() => ({ componentsNeedsUpdateOnNextRender })),
flushComponentsNeedsUpdateOnNextRender: () => set(() => ({ componentsNeedsUpdateOnNextRender: [] })),