mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-24 09:28:31 +00:00
fixes: component -extra re-rendering issue on current state changes.
This commit is contained in:
parent
858dff3606
commit
9b88e9f8b2
7 changed files with 148 additions and 136 deletions
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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: [] })),
|
||||
|
|
|
|||
Loading…
Reference in a new issue