Page MenuHomePhorge

D6642.1768431220.diff
No OneTemporary

Size
2 KB
Referenced Files
None
Subscribers
None

D6642.1768431220.diff

diff --git a/native/components/selectable-text-input.react.js b/native/components/selectable-text-input.react.js
--- a/native/components/selectable-text-input.react.js
+++ b/native/components/selectable-text-input.react.js
@@ -2,6 +2,7 @@
import * as React from 'react';
+import type { SelectionChangeEvent } from '../types/react-native';
import ClearableTextInput from './clearable-text-input.react';
import type {
SelectableTextInputProps,
@@ -12,7 +13,20 @@
props,
ref,
): React.Node {
- const { clearableTextInputRef, onUpdateSyncedSelectionData, ...rest } = props;
+ const {
+ clearableTextInputRef,
+ onUpdateSyncedSelectionData,
+ onSelectionChange,
+ selection,
+ ...rest
+ } = props;
+
+ // React Native doesn't handle controlled selection well, so we only set the
+ // selection prop when we need to mutate the selection
+ // https://github.com/facebook/react-native/issues/29063
+ const [controlSelection, setControlSelection] = React.useState<boolean>(
+ false,
+ );
const clearableTextInputRefCallback = React.useCallback(
(clearableTextInput: ?React.ElementRef<typeof ClearableTextInput>) => {
@@ -21,17 +35,45 @@
[clearableTextInputRef],
);
- const prepareForSelectionMutation = React.useCallback(() => {}, []);
+ const prepareForSelectionMutation = React.useCallback(
+ () => setControlSelection(true),
+ [],
+ );
const ourRef = React.useMemo(
() => ({
prepareForSelectionMutation,
}),
[prepareForSelectionMutation],
);
-
React.useImperativeHandle(ref, () => ourRef, [ourRef]);
- return <ClearableTextInput {...rest} ref={clearableTextInputRefCallback} />;
+ // - It's important for us to keep text and selection state in sync, since
+ // upstream code in ChatInputBar processes this data during render to
+ // generate a list of @-mention suggestions
+ // - On Android, text change events precede selection events, and each leads
+ // to a separate React render cycle
+ // - To prevent render cycles where the data isn't in sync, we defer text
+ // change events until the corresponding selection event comes in
+ const onSelectionChangeOverride = React.useCallback(
+ (event: SelectionChangeEvent) => {
+ setControlSelection(false);
+ onSelectionChange?.(event);
+ onUpdateSyncedSelectionData({
+ text: props.value,
+ selection: event.nativeEvent.selection,
+ });
+ },
+ [onUpdateSyncedSelectionData, props.value, onSelectionChange],
+ );
+
+ return (
+ <ClearableTextInput
+ {...rest}
+ onSelectionChange={onSelectionChangeOverride}
+ selection={controlSelection ? selection : undefined}
+ ref={clearableTextInputRefCallback}
+ />
+ );
});
const MemoizedSelectableTextInput: React.AbstractComponent<

File Metadata

Mime Type
text/plain
Expires
Wed, Jan 14, 10:53 PM (11 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5933374
Default Alt Text
D6642.1768431220.diff (2 KB)

Event Timeline