From 71e8784f4252dab8962f849a4cfb809bcb874826 Mon Sep 17 00:00:00 2001 From: Carl Smith <5456533+CarlosNZ@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:22:42 +1200 Subject: [PATCH 1/4] Fix null enter bug --- src/ValueNodes.tsx | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/ValueNodes.tsx b/src/ValueNodes.tsx index 43a861e..575551f 100644 --- a/src/ValueNodes.tsx +++ b/src/ValueNodes.tsx @@ -286,27 +286,34 @@ export const BooleanValue: React.FC = ({ ) } -// A custom hook to add a keyboard listener to a component that does't have +// A custom hook to add a keyboard listener to a component that doesn't have // standard DOM keyboard behaviour (like inputs). Only used for the `null` // component here, but is exported for re-use with Custom Components if required export const useKeyboardListener = (isEditing: boolean, listener: (e: unknown) => void) => { const timer = useRef(undefined) + const currentListener = useRef(listener) + + // Always update the ref to point to the latest listener + useEffect(() => { + currentListener.current = listener + }, [listener]) useEffect(() => { if (!isEditing) { - // The listener messes with other elements when switching rapidly (e.g. - // when "getNext" is called repeatedly on inaccessible elements), so we - // cancel the listener load before it even happens if this node gets - // switched from isEditing to not in less than 100ms window.clearTimeout(timer.current) return } - // Small delay to prevent registering keyboard input from previous element - // if switched using "Tab" - timer.current = window.setTimeout(() => window.addEventListener('keydown', listener), 100) - return () => window.removeEventListener('keydown', listener) - }, [isEditing, listener]) + // Create a stable reference to the event handler that always calls + // the latest listener + const eventHandler = (e: unknown) => currentListener.current(e) + + timer.current = window.setTimeout(() => { + window.addEventListener('keydown', eventHandler) + }, 100) + + return () => window.removeEventListener('keydown', eventHandler) + }, [isEditing]) } export const NullValue: React.FC = ({ From 09aeb41707aff2772c2f667e66fcb2e15b4f1325 Mon Sep 17 00:00:00 2001 From: Carl Smith <5456533+CarlosNZ@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:53:19 +1200 Subject: [PATCH 2/4] Fix listener remaining after dismount --- src/ValueNodes.tsx | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/ValueNodes.tsx b/src/ValueNodes.tsx index 575551f..f6022bb 100644 --- a/src/ValueNodes.tsx +++ b/src/ValueNodes.tsx @@ -298,21 +298,31 @@ export const useKeyboardListener = (isEditing: boolean, listener: (e: unknown) = currentListener.current = listener }, [listener]) + // Define our stable event handler function + const eventHandler = (e: unknown) => { + currentListener.current(e) + } + useEffect(() => { - if (!isEditing) { - window.clearTimeout(timer.current) - return - } + // The listener messes with other elements when switching rapidly (e.g. when + // "getNext" is called repeatedly on inaccessible elements), so we cancel + // the listener load before it even happens if this node gets switched from + // isEditing to not in less than 100ms + window.clearTimeout(timer.current) - // Create a stable reference to the event handler that always calls - // the latest listener - const eventHandler = (e: unknown) => currentListener.current(e) + if (!isEditing) return + // Small delay to prevent registering keyboard input from previous element + // if switched using "Tab" timer.current = window.setTimeout(() => { window.addEventListener('keydown', eventHandler) }, 100) - return () => window.removeEventListener('keydown', eventHandler) + // Cleanup function + return () => { + window.clearTimeout(timer.current) + window.removeEventListener('keydown', eventHandler) + } }, [isEditing]) } From 5928365c337732174f7a8b64f7b54ab9c68d9f69 Mon Sep 17 00:00:00 2001 From: Carl Smith <5456533+CarlosNZ@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:59:15 +1200 Subject: [PATCH 3/4] Prevent React warning when boolean value is null --- src/ValueNodes.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ValueNodes.tsx b/src/ValueNodes.tsx index f6022bb..c8e1387 100644 --- a/src/ValueNodes.tsx +++ b/src/ValueNodes.tsx @@ -256,6 +256,8 @@ export const BooleanValue: React.FC = ({ }) => { const { getStyles } = useTheme() + if (typeof value !== 'boolean') return null + return isEditing ? ( Date: Thu, 24 Apr 2025 14:01:40 +1200 Subject: [PATCH 4/4] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ea1fa41..4eee143 100644 --- a/README.md +++ b/README.md @@ -1216,6 +1216,7 @@ This component is heavily inspired by [react-json-view](https://github.com/mac-s ## Changelog +- **1.26.1**: Fix bug when submitting with keyboard after switching to `null` type ([#194](https://github.com/CarlosNZ/json-edit-react/pull/194)) - **1.26.0**: - Handle non-standard data types (e.g. `undefined`, `BigInt`) when stringifying/parsing JSON - More custom components (See [library ReadMe](https://github.com/CarlosNZ/json-edit-react/blob/main/custom-component-library/README.md))