1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-05 05:45:22 +02:00

fix(app/editor): reduce editor slowness by debouncing onChange calls (#326)

This commit is contained in:
LP B 2025-01-17 22:41:06 +01:00 committed by GitHub
parent cab667c23b
commit db48da185a
4 changed files with 177 additions and 98 deletions

View file

@ -0,0 +1,52 @@
/* eslint-disable no-console */
import { intersection } from 'lodash';
import { useEffect, useRef } from 'react';
function logPropDifferences(
newProps: Record<string, unknown>,
lastProps: Record<string, unknown>,
verbose: boolean
) {
const allKeys = intersection(Object.keys(newProps), Object.keys(lastProps));
const changedKeys: string[] = [];
allKeys.forEach((key) => {
const newValue = newProps[key];
const lastValue = lastProps[key];
if (newValue !== lastValue) {
changedKeys.push(key);
}
});
if (changedKeys.length) {
if (verbose) {
changedKeys.forEach((key) => {
const newValue = newProps[key];
const lastValue = lastProps[key];
console.log('Key [', key, '] changed');
console.log('From: ', lastValue);
console.log('To: ', newValue);
console.log('------');
});
} else {
console.log('Changed keys: ', changedKeys.join());
}
}
}
export function useDebugPropChanges(
newProps: Record<string, unknown>,
verbose: boolean = true
) {
const lastProps = useRef<Record<string, unknown>>();
// Should only run when the component re-mounts
useEffect(() => {
console.log('Mounted');
}, []);
if (lastProps.current) {
logPropDifferences(newProps, lastProps.current, verbose);
}
lastProps.current = newProps;
}
/* eslint-enable no-console */

View file

@ -3,7 +3,7 @@ import { StreamLanguage, LanguageSupport } from '@codemirror/language';
import { yaml } from '@codemirror/legacy-modes/mode/yaml';
import { dockerFile } from '@codemirror/legacy-modes/mode/dockerfile';
import { shell } from '@codemirror/legacy-modes/mode/shell';
import { useMemo, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { createTheme } from '@uiw/codemirror-themes';
import { tags as highlightTags } from '@lezer/highlight';
@ -11,6 +11,8 @@ import { AutomationTestingProps } from '@/types';
import { CopyButton } from '@@/buttons/CopyButton';
import { useDebounce } from '../hooks/useDebounce';
import styles from './CodeEditor.module.css';
import { TextTip } from './Tip/TextTip';
import { StackVersionSelector } from './StackVersionSelector';
@ -89,17 +91,17 @@ export function CodeEditor({
return extensions;
}, [type]);
function handleVersionChange(version: number) {
if (versions && versions.length > 1) {
if (version < versions[0]) {
setIsRollback(true);
} else {
setIsRollback(false);
const handleVersionChange = useCallback(
(version: number) => {
if (versions && versions.length > 1) {
setIsRollback(version < versions[0]);
}
}
onVersionChange?.(version);
},
[onVersionChange, versions]
);
onVersionChange?.(version);
}
const [debouncedValue, debouncedOnChange] = useDebounce(value, onChange);
return (
<>
@ -136,8 +138,8 @@ export function CodeEditor({
<CodeMirror
className={styles.root}
theme={theme}
value={value}
onChange={onChange}
value={debouncedValue}
onChange={debouncedOnChange}
readOnly={readonly || isRollback}
id={id}
extensions={extensions}

View file

@ -1,5 +1,5 @@
import { FormikErrors, useFormikContext } from 'formik';
import { SetStateAction } from 'react';
import { SetStateAction, useCallback } from 'react';
import { GitForm } from '@/react/portainer/gitops/GitForm';
import { baseEdgeStackWebhookUrl } from '@/portainer/helpers/webhookHelper';
@ -37,6 +37,23 @@ export function DockerComposeForm({ webhookId, onChangeTemplate }: Props) {
const { errors, values, setValues } = useFormikContext<DockerFormValues>();
const { method } = values;
const handleChange = useCallback(
(newValues: Partial<DockerFormValues>) => {
setValues((values) => ({
...values,
...newValues,
}));
},
[setValues]
);
const saveFileContent = useCallback(
(value: string) => {
handleChange({ fileContent: value });
},
[handleChange]
);
return (
<>
<FormSection title="Build Method">
@ -91,7 +108,7 @@ export function DockerComposeForm({ webhookId, onChangeTemplate }: Props) {
{method === editor.value && (
<DockerContentField
value={values.fileContent}
onChange={(value) => handleChange({ fileContent: value })}
onChange={saveFileContent}
error={errors?.fileContent}
/>
)}
@ -145,13 +162,6 @@ export function DockerComposeForm({ webhookId, onChangeTemplate }: Props) {
)}
</>
);
function handleChange(newValues: Partial<DockerFormValues>) {
setValues((values) => ({
...values,
...newValues,
}));
}
}
type TemplateContentFieldProps = {