Changeset View
Changeset View
Standalone View
Standalone View
native/components/selectable-text-input.react.js
// @flow | // @flow | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import type { SelectionChangeEvent } from '../types/react-native'; | |||||
import ClearableTextInput from './clearable-text-input.react'; | import ClearableTextInput from './clearable-text-input.react'; | ||||
import type { | import type { | ||||
SelectableTextInputProps, | SelectableTextInputProps, | ||||
SelectableTextInputRef, | SelectableTextInputRef, | ||||
} from './selectable-text-input'; | } from './selectable-text-input'; | ||||
const SelectableTextInput = React.forwardRef(function BaseSelectableTextInput( | const SelectableTextInput = React.forwardRef(function BaseSelectableTextInput( | ||||
props, | props, | ||||
ref, | ref, | ||||
): React.Node { | ): 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( | const clearableTextInputRefCallback = React.useCallback( | ||||
(clearableTextInput: ?React.ElementRef<typeof ClearableTextInput>) => { | (clearableTextInput: ?React.ElementRef<typeof ClearableTextInput>) => { | ||||
clearableTextInputRef(clearableTextInput); | clearableTextInputRef(clearableTextInput); | ||||
}, | }, | ||||
[clearableTextInputRef], | [clearableTextInputRef], | ||||
); | ); | ||||
const prepareForSelectionMutation = React.useCallback(() => {}, []); | const prepareForSelectionMutation = React.useCallback( | ||||
() => setControlSelection(true), | |||||
[], | |||||
); | |||||
const ourRef = React.useMemo( | const ourRef = React.useMemo( | ||||
() => ({ | () => ({ | ||||
prepareForSelectionMutation, | prepareForSelectionMutation, | ||||
}), | }), | ||||
[prepareForSelectionMutation], | [prepareForSelectionMutation], | ||||
); | ); | ||||
React.useImperativeHandle(ref, () => ourRef, [ourRef]); | 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); | |||||
przemek: It looks like we're not using onSelection when creating this component in `ChatInputBar` Is it… | |||||
ashoatAuthorUnsubmitted Done Inline ActionsYes, we don't want to break compatibility. You can see we forward all TextInput props... SelectableTextInput is meant as a drop-in replacement for TextInput that addresses issues with selectable text ashoat: Yes, we don't want to break compatibility. You can see we forward all `TextInput` props... | |||||
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< | const MemoizedSelectableTextInput: React.AbstractComponent< | ||||
SelectableTextInputProps, | SelectableTextInputProps, | ||||
SelectableTextInputRef, | SelectableTextInputRef, | ||||
> = React.memo<SelectableTextInputProps, SelectableTextInputRef>( | > = React.memo<SelectableTextInputProps, SelectableTextInputRef>( | ||||
SelectableTextInput, | SelectableTextInput, | ||||
); | ); | ||||
export default MemoizedSelectableTextInput; | export default MemoizedSelectableTextInput; |
It looks like we're not using onSelection when creating this component in ChatInputBar Is it there to keep compatibility with TextInput API (i.e. someone sometime in future may want to use that normal callback) or is there something I missed?