Skip to content

Commit f3e9a7a

Browse files
authored
feat: flushable debounce in react-material
Some React Material inputs are debounced to avoid redundant update cycles. This debounce is now flushed immediately once the corresponding input loses focus.
1 parent 393c4dc commit f3e9a7a

File tree

7 files changed

+82
-19
lines changed

7 files changed

+82
-19
lines changed

packages/material-renderers/src/controls/MaterialAnyOfStringOrEnumControl.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ import { Control, withJsonFormsControlProps } from '@jsonforms/react';
3838
import { InputBaseComponentProps } from '@mui/material';
3939
import merge from 'lodash/merge';
4040
import React, { useMemo } from 'react';
41-
import { useDebouncedChange, useInputComponent, WithInputProps } from '../util';
41+
import {
42+
useDebouncedChange,
43+
useInputComponent,
44+
WithInputProps,
45+
useFocus,
46+
} from '../util';
4247
import { MaterialInputControl } from './MaterialInputControl';
4348

4449
const findEnumSchema = (schemas: JsonSchema[]) =>
@@ -51,6 +56,7 @@ const findTextSchema = (schemas: JsonSchema[]) =>
5156
const MuiAutocompleteInputText = (
5257
props: EnumCellProps & WithClassname & WithInputProps
5358
) => {
59+
const [focused, onFocus, onBlur] = useFocus();
5460
const {
5561
data,
5662
config,
@@ -87,7 +93,11 @@ const MuiAutocompleteInputText = (
8793
handleChange,
8894
'',
8995
data,
90-
path
96+
path,
97+
undefined,
98+
undefined,
99+
true,
100+
focused
91101
);
92102

93103
const dataList = (
@@ -102,6 +112,8 @@ const MuiAutocompleteInputText = (
102112
type='text'
103113
value={inputText}
104114
onChange={onChange}
115+
onFocus={onFocus}
116+
onBlur={onBlur}
105117
className={className}
106118
id={id}
107119
label={label}

packages/material-renderers/src/controls/MaterialNativeControl.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
/*
22
The MIT License
3-
3+
44
Copyright (c) 2017-2019 EclipseSource Munich
55
https://github.com/eclipsesource/jsonforms
6-
6+
77
Permission is hereby granted, free of charge, to any person obtaining a copy
88
of this software and associated documentation files (the "Software"), to deal
99
in the Software without restriction, including without limitation the rights
1010
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1111
copies of the Software, and to permit persons to whom the Software is
1212
furnished to do so, subject to the following conditions:
13-
13+
1414
The above copyright notice and this permission notice shall be included in
1515
all copies or substantial portions of the Software.
16-
16+
1717
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1818
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1919
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -60,7 +60,11 @@ export const MaterialNativeControl = (props: ControlProps) => {
6060
handleChange,
6161
'',
6262
data,
63-
path
63+
path,
64+
undefined,
65+
undefined,
66+
true,
67+
focused
6468
);
6569
const fieldType = appliedUiSchemaOptions.format ?? schema.format;
6670
const showDescription = !isDescriptionHidden(

packages/material-renderers/src/mui-controls/MuiInputInteger.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@
2525
import React from 'react';
2626
import { CellProps, WithClassname } from '@jsonforms/core';
2727
import merge from 'lodash/merge';
28-
import { useDebouncedChange, useInputComponent, WithInputProps } from '../util';
28+
import {
29+
useDebouncedChange,
30+
useInputComponent,
31+
WithInputProps,
32+
useFocus,
33+
} from '../util';
2934

3035
const toNumber = (value: string) =>
3136
value === '' ? undefined : parseInt(value, 10);
@@ -34,6 +39,7 @@ const eventToValue = (ev: any) => toNumber(ev.target.value);
3439
export const MuiInputInteger = React.memo(function MuiInputInteger(
3540
props: CellProps & WithClassname & WithInputProps
3641
) {
42+
const [focused, onFocus, onBlur] = useFocus();
3743
const {
3844
data,
3945
className,
@@ -56,14 +62,19 @@ export const MuiInputInteger = React.memo(function MuiInputInteger(
5662
'',
5763
data,
5864
path,
59-
eventToValue
65+
eventToValue,
66+
undefined,
67+
true,
68+
focused
6069
);
6170

6271
return (
6372
<InputComponent
6473
label={label}
6574
type='number'
6675
value={inputValue}
76+
onFocus={onFocus}
77+
onBlur={onBlur}
6778
onChange={onChange}
6879
className={className}
6980
id={id}

packages/material-renderers/src/mui-controls/MuiInputNumber.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,20 @@
2525
import React from 'react';
2626
import { CellProps, WithClassname } from '@jsonforms/core';
2727
import merge from 'lodash/merge';
28-
import { useDebouncedChange, useInputComponent, WithInputProps } from '../util';
28+
import {
29+
useDebouncedChange,
30+
useInputComponent,
31+
WithInputProps,
32+
useFocus,
33+
} from '../util';
2934

3035
const toNumber = (value: string) =>
3136
value === '' ? undefined : parseFloat(value);
3237
const eventToValue = (ev: any) => toNumber(ev.target.value);
3338
export const MuiInputNumber = React.memo(function MuiInputNumber(
3439
props: CellProps & WithClassname & WithInputProps
3540
) {
41+
const [focused, onFocus, onBlur] = useFocus();
3642
const {
3743
data,
3844
className,
@@ -54,7 +60,10 @@ export const MuiInputNumber = React.memo(function MuiInputNumber(
5460
'',
5561
data,
5662
path,
57-
eventToValue
63+
eventToValue,
64+
undefined,
65+
true,
66+
focused
5867
);
5968

6069
return (
@@ -63,6 +72,8 @@ export const MuiInputNumber = React.memo(function MuiInputNumber(
6372
label={label}
6473
value={inputValue}
6574
onChange={onChange}
75+
onFocus={onFocus}
76+
onBlur={onBlur}
6677
className={className}
6778
id={id}
6879
disabled={!enabled}

packages/material-renderers/src/mui-controls/MuiInputNumberFormat.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,17 @@
2525
import React, { useCallback } from 'react';
2626
import { CellProps, Formatted, WithClassname } from '@jsonforms/core';
2727
import merge from 'lodash/merge';
28-
import { useDebouncedChange, useInputComponent, WithInputProps } from '../util';
28+
import {
29+
useDebouncedChange,
30+
useInputComponent,
31+
WithInputProps,
32+
useFocus,
33+
} from '../util';
2934

3035
export const MuiInputNumberFormat = React.memo(function MuiInputNumberFormat(
3136
props: CellProps & WithClassname & Formatted<number> & WithInputProps
3237
) {
38+
const [focused, onFocus, onBlur] = useFocus();
3339
const {
3440
className,
3541
id,
@@ -62,14 +68,19 @@ export const MuiInputNumberFormat = React.memo(function MuiInputNumberFormat(
6268
'',
6369
formattedNumber,
6470
path,
65-
validStringNumber
71+
validStringNumber,
72+
undefined,
73+
true,
74+
focused
6675
);
6776

6877
return (
6978
<InputComponent
7079
type='text'
7180
value={inputValue}
7281
onChange={onChange}
82+
onFocus={onFocus}
83+
onBlur={onBlur}
7384
className={className}
7485
id={id}
7586
label={label}

packages/material-renderers/src/mui-controls/MuiInputText.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
WithInputProps,
3939
useDebouncedChange,
4040
useInputComponent,
41+
useFocus,
4142
} from '../util';
4243

4344
interface MuiTextInputProps {
@@ -51,6 +52,7 @@ const eventToValue = (ev: any) =>
5152
export const MuiInputText = React.memo(function MuiInputText(
5253
props: CellProps & WithClassname & MuiTextInputProps & WithInputProps
5354
) {
55+
const [focused, onFocus, onBlur] = useFocus();
5456
const [showAdornment, setShowAdornment] = useState(false);
5557
const {
5658
data,
@@ -88,7 +90,10 @@ export const MuiInputText = React.memo(function MuiInputText(
8890
'',
8991
data,
9092
path,
91-
eventToValue
93+
eventToValue,
94+
undefined,
95+
true,
96+
focused
9297
);
9398
const onPointerEnter = () => setShowAdornment(true);
9499
const onPointerLeave = () => setShowAdornment(false);
@@ -109,6 +114,8 @@ export const MuiInputText = React.memo(function MuiInputText(
109114
value={inputText}
110115
onChange={onChange}
111116
className={className}
117+
onBlur={onBlur}
118+
onFocus={onFocus}
112119
id={id}
113120
disabled={!enabled}
114121
autoFocus={appliedUiSchemaOptions.focus}

packages/material-renderers/src/util/debounce.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
/*
22
The MIT License
3-
3+
44
Copyright (c) 2021 EclipseSource Munich
55
https://github.com/eclipsesource/jsonforms
6-
6+
77
Permission is hereby granted, free of charge, to any person obtaining a copy
88
of this software and associated documentation files (the "Software"), to deal
99
in the Software without restriction, including without limitation the rights
1010
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1111
copies of the Software, and to permit persons to whom the Software is
1212
furnished to do so, subject to the following conditions:
13-
13+
1414
The above copyright notice and this permission notice shall be included in
1515
all copies or substantial portions of the Software.
16-
16+
1717
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1818
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1919
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -32,7 +32,9 @@ export const useDebouncedChange = (
3232
data: any,
3333
path: string,
3434
eventToValueFunction: (ev: any) => any = eventToValue,
35-
timeout = 300
35+
timeout = 300,
36+
flushOnBlur = false,
37+
focused = false
3638
): [any, React.ChangeEventHandler, () => void] => {
3739
const [input, setInput] = useState(data ?? defaultValue);
3840
useEffect(() => {
@@ -42,6 +44,11 @@ export const useDebouncedChange = (
4244
debounce((newValue: string) => handleChange(path, newValue), timeout),
4345
[handleChange, path, timeout]
4446
);
47+
useEffect(() => {
48+
if (!focused && flushOnBlur) {
49+
debouncedUpdate.flush();
50+
}
51+
}, [focused, flushOnBlur, debouncedUpdate]);
4552
const onChange = useCallback(
4653
(ev: any) => {
4754
const newValue = eventToValueFunction(ev);

0 commit comments

Comments
 (0)