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:
parent
cab667c23b
commit
db48da185a
4 changed files with 177 additions and 98 deletions
52
app/react-tools/useDebugPropChanges.ts
Normal file
52
app/react-tools/useDebugPropChanges.ts
Normal 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 */
|
|
@ -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}
|
||||
|
|
|
@ -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 = {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue