fix: prevent useInput effect churn with inline handlers (#265)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2025-12-17 14:30:17 -08:00
committed by GitHub
parent d4682421b6
commit 8eda838879

View File

@@ -1,12 +1,17 @@
import { useEffect } from 'react'; import { useEffect, useRef } from 'react';
import parseKeypress, { nonAlphanumericKeys } from '../parse-keypress.js'; import parseKeypress, { nonAlphanumericKeys } from '../parse-keypress.js';
import reconciler from '../reconciler.js'; import reconciler from '../reconciler.js';
import useStdin from './use-stdin.js'; import useStdin from './use-stdin.js';
// Patched for bracketed paste: propagate "isPasted" and avoid leaking ESC sequences // Patched for bracketed paste: propagate "isPasted" and avoid leaking ESC sequences
// Also patched to use ref for inputHandler to avoid effect churn with inline handlers
const useInput = (inputHandler, options = {}) => { const useInput = (inputHandler, options = {}) => {
const { stdin, setRawMode, internal_exitOnCtrlC, internal_eventEmitter } = useStdin(); const { stdin, setRawMode, internal_exitOnCtrlC, internal_eventEmitter } = useStdin();
// Store handler in ref to avoid re-subscribing when handler identity changes
const handlerRef = useRef(inputHandler);
handlerRef.current = inputHandler;
useEffect(() => { useEffect(() => {
if (options.isActive === false) { if (options.isActive === false) {
return; return;
@@ -43,7 +48,7 @@ const useInput = (inputHandler, options = {}) => {
isPasted: true isPasted: true
}; };
reconciler.batchedUpdates(() => { reconciler.batchedUpdates(() => {
inputHandler(data.sequence || data.raw || '', key); handlerRef.current(data.sequence || data.raw || '', key);
}); });
return; return;
} }
@@ -84,7 +89,7 @@ const useInput = (inputHandler, options = {}) => {
if (!(input === 'c' && key.ctrl) || !internal_exitOnCtrlC) { if (!(input === 'c' && key.ctrl) || !internal_exitOnCtrlC) {
reconciler.batchedUpdates(() => { reconciler.batchedUpdates(() => {
inputHandler(input, key); handlerRef.current(input, key);
}); });
} }
}; };
@@ -93,7 +98,7 @@ const useInput = (inputHandler, options = {}) => {
return () => { return () => {
internal_eventEmitter?.removeListener('input', handleData); internal_eventEmitter?.removeListener('input', handleData);
}; };
}, [options.isActive, stdin, internal_exitOnCtrlC, inputHandler]); }, [options.isActive, stdin, internal_exitOnCtrlC]);
}; };
export default useInput; export default useInput;