diff --git a/src/TransformerTextInput.tsx b/src/TransformerTextInput.tsx index fd6a7fd..d9a175a 100644 --- a/src/TransformerTextInput.tsx +++ b/src/TransformerTextInput.tsx @@ -61,13 +61,34 @@ export type TransformerTextInputProps = Omit & { export const TransformerTextInput = forwardRef( ( - { transformer, onChangeText, ...others }: TransformerTextInputProps, + { + transformer, + onChangeText, + defaultValue, + ...others + }: TransformerTextInputProps, forwardedRef: Ref, ) => { const transformerId = useMemo(() => { return registerTransformer(transformer); }, [transformer]); + // Pre-transform defaultValue on the JS thread so Yoga measures the correct + // text from the start. Without this the native-side transformation happens + // after layout and doesn't trigger a remeasure. + const transformedDefaultValue = useMemo(() => { + if (defaultValue == null) { + return undefined; + } + const result = transformer.worklet({ + value: defaultValue, + previousValue: defaultValue, + selection: { start: defaultValue.length, end: defaultValue.length }, + previousSelection: { start: 0, end: 0 }, + }); + return result?.value ?? defaultValue; + }, [defaultValue, transformer]); + useEffect(() => { return () => { unregisterTransformer(transformerId); @@ -135,6 +156,7 @@ export const TransformerTextInput = forwardRef( // @ts-expect-error ref={inputRef} onChangeText={handleChangeText} + defaultValue={transformedDefaultValue} {...others} /> diff --git a/src/registry.ts b/src/registry.ts index ed3ee03..00a975f 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -1,4 +1,4 @@ -import { runOnUI } from 'react-native-worklets'; +import { runOnUI, executeOnUIRuntimeSync } from 'react-native-worklets'; import NativeTransformerTextInputModule from './NativeTransformerTextInputModule'; import { type Selection, type Transformer } from './Transformer'; import { computeUncontrolledSelection, validateSelection } from './selection'; @@ -29,8 +29,9 @@ function initializeIfNeeded() { return; } - // Important that `runOnUI` is called first to make sure the UI runtime is initialized. - runOnUI(() => { + // Set up registry on UI runtime synchronously so it is guaranteed to exist + // when native code accesses it after install(). + executeOnUIRuntimeSync(() => { 'worklet'; const transformersMap = new Map();