diff --git a/.changeset/long-ducks-do.md b/.changeset/long-ducks-do.md new file mode 100644 index 0000000000..b956db24e4 --- /dev/null +++ b/.changeset/long-ducks-do.md @@ -0,0 +1,5 @@ +--- +"@nextui-org/input": patch +--- + +syncs changes to ref value to internal (state) value (#3024, #3436) diff --git a/packages/components/input/__tests__/input.test.tsx b/packages/components/input/__tests__/input.test.tsx index 043de2da6e..8022fffcdb 100644 --- a/packages/components/input/__tests__/input.test.tsx +++ b/packages/components/input/__tests__/input.test.tsx @@ -126,6 +126,26 @@ describe("Input", () => { expect(ref.current?.value)?.toBe(value); }); + it("setting ref should sync the internal value", () => { + const ref = React.createRef(); + + const {container} = render(); + + if (!ref.current) { + throw new Error("ref is null"); + } + + ref.current!.value = "value"; + + const input = container.querySelector("input")!; + + input.focus(); + + const internalValue = input.value; + + expect(ref.current?.value)?.toBe(internalValue); + }); + it("should clear the value and onClear is triggered", async () => { const onClear = jest.fn(); diff --git a/packages/components/input/src/use-input.ts b/packages/components/input/src/use-input.ts index 0cc2102725..d6cd63b780 100644 --- a/packages/components/input/src/use-input.ts +++ b/packages/components/input/src/use-input.ts @@ -15,7 +15,7 @@ import {useDOMRef, filterDOMProps} from "@nextui-org/react-utils"; import {useFocusWithin, useHover, usePress} from "@react-aria/interactions"; import {clsx, dataAttr, isEmpty, objectToDeps, safeAriaLabel, warn} from "@nextui-org/shared-utils"; import {useControlledState} from "@react-stately/utils"; -import {useMemo, Ref, useCallback, useState} from "react"; +import {useMemo, Ref, useCallback, useState, useImperativeHandle, useRef} from "react"; import {chain, mergeProps} from "@react-aria/utils"; import {useTextField} from "@react-aria/textfield"; @@ -131,7 +131,41 @@ export function useInput(ref); + const domRef = useRef(null); + + let proxy: T | undefined = undefined; + + useImperativeHandle( + ref, + () => { + if (proxy === undefined) { + proxy = new Proxy(domRef.current!, { + get(target, prop) { + const value = target[prop]; + + if (value instanceof Function) { + return value.bind(target); + } + + return value; + }, + set(target, prop, value) { + target[prop] = value; + + if (prop === "value") { + setInputValue(value); + } + + return true; + }, + }); + } + + return proxy; + }, + [domRef.current], + ); + const baseDomRef = useDOMRef(baseRef); const inputWrapperRef = useDOMRef(wrapperRef); const innerWrapperRef = useDOMRef(innerWrapperRefProp);