From c2d4fd6d77f1d594fe4d1853ab5c511adda29d45 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Mon, 18 May 2020 12:08:06 -0700 Subject: [PATCH] Fix TextInput onSelectionChange logic --- .../TextInput/TextInput.stories.mdx | 6 + .../TextInput/examples/OnSelectionChange.js | 117 +++--------------- .../examples/OnSelectionChangeControlled.js | 107 ++++++++++++++++ .../components/TextInput/examples/index.js | 1 + .../src/exports/TextInput/index.js | 36 +++--- 5 files changed, 148 insertions(+), 119 deletions(-) create mode 100644 packages/docs/src/components/TextInput/examples/OnSelectionChangeControlled.js diff --git a/packages/docs/src/components/TextInput/TextInput.stories.mdx b/packages/docs/src/components/TextInput/TextInput.stories.mdx index 7ac57051..d0ff1118 100644 --- a/packages/docs/src/components/TextInput/TextInput.stories.mdx +++ b/packages/docs/src/components/TextInput/TextInput.stories.mdx @@ -209,6 +209,12 @@ called with `{ nativeEvent: { selection: { start, end } } }`. + + + + + + ### onSubmitEditing Callback that is called when the keyboard's submit button is pressed. When diff --git a/packages/docs/src/components/TextInput/examples/OnSelectionChange.js b/packages/docs/src/components/TextInput/examples/OnSelectionChange.js index 82b4bbd1..4a4957de 100644 --- a/packages/docs/src/components/TextInput/examples/OnSelectionChange.js +++ b/packages/docs/src/components/TextInput/examples/OnSelectionChange.js @@ -2,106 +2,27 @@ * @noflow */ -import React from 'react'; +import React, { useState } from 'react'; +import { View, Text, TextInput } from 'react-native'; import { styles } from '../helpers'; -import { Text, TextInput, View } from 'react-native'; -type SelectionExampleState = { - selection: { - start: number, - end?: number - }, - value: string -}; +function OnSelectionChange() { + const [text, setText] = useState(''); + const [selection, setSelection] = useState({ start: 0, end: 0 }); -class OnSelectionChangeExample extends React.Component { - state: SelectionExampleState; - - _textInput: any; - - constructor(props) { - super(props); - this.state = { - selection: { start: 0, end: 0 }, - value: props.value - }; - } - - onSelectionChange = ({ nativeEvent: { selection } }) => { - this.setState({ selection }); - }; - - onChangeText = value => { - this.setState({ value }); - }; - - getRandomPosition() { - const length = this.state.value.length; - return Math.round(Math.random() * length); - } - - select = (start, end) => () => { - this._textInput.focus(); - this.setState({ selection: { start, end } }); - }; - - selectRandom = () => { - const positions = [this.getRandomPosition(), this.getRandomPosition()].sort(); - this.select(...positions)(); - }; - - placeAt = position => () => { - this.select(position, position)(); - }; - - placeAtRandom = () => { - this.placeAt(this.getRandomPosition())(); - }; - - setRef = textInput => { - this._textInput = textInput; - }; - - render() { - const length = this.state.value.length; - - return ( - - - - selection = {JSON.stringify(this.state.selection)} - Place at Start (0, 0) - - Place at End ({length}, {length}) - - Place at Random - Select All - Select Random - - - ); - } + return ( + + { + setSelection(event.nativeEvent.selection); + }} + style={styles.textinput} + value={text} + /> + {JSON.stringify(selection)} + + ); } -const TextInputOnSelectionChangeExample = () => ( - - - - -); - -export default function OnSelectionChange() { - return ; -} +export default OnSelectionChange; diff --git a/packages/docs/src/components/TextInput/examples/OnSelectionChangeControlled.js b/packages/docs/src/components/TextInput/examples/OnSelectionChangeControlled.js new file mode 100644 index 00000000..82b4bbd1 --- /dev/null +++ b/packages/docs/src/components/TextInput/examples/OnSelectionChangeControlled.js @@ -0,0 +1,107 @@ +/** + * @noflow + */ + +import React from 'react'; +import { styles } from '../helpers'; +import { Text, TextInput, View } from 'react-native'; + +type SelectionExampleState = { + selection: { + start: number, + end?: number + }, + value: string +}; + +class OnSelectionChangeExample extends React.Component { + state: SelectionExampleState; + + _textInput: any; + + constructor(props) { + super(props); + this.state = { + selection: { start: 0, end: 0 }, + value: props.value + }; + } + + onSelectionChange = ({ nativeEvent: { selection } }) => { + this.setState({ selection }); + }; + + onChangeText = value => { + this.setState({ value }); + }; + + getRandomPosition() { + const length = this.state.value.length; + return Math.round(Math.random() * length); + } + + select = (start, end) => () => { + this._textInput.focus(); + this.setState({ selection: { start, end } }); + }; + + selectRandom = () => { + const positions = [this.getRandomPosition(), this.getRandomPosition()].sort(); + this.select(...positions)(); + }; + + placeAt = position => () => { + this.select(position, position)(); + }; + + placeAtRandom = () => { + this.placeAt(this.getRandomPosition())(); + }; + + setRef = textInput => { + this._textInput = textInput; + }; + + render() { + const length = this.state.value.length; + + return ( + + + + selection = {JSON.stringify(this.state.selection)} + Place at Start (0, 0) + + Place at End ({length}, {length}) + + Place at Random + Select All + Select Random + + + ); + } +} + +const TextInputOnSelectionChangeExample = () => ( + + + + +); + +export default function OnSelectionChange() { + return ; +} diff --git a/packages/docs/src/components/TextInput/examples/index.js b/packages/docs/src/components/TextInput/examples/index.js index f71a70fe..176dcd49 100644 --- a/packages/docs/src/components/TextInput/examples/index.js +++ b/packages/docs/src/components/TextInput/examples/index.js @@ -9,6 +9,7 @@ export { default as maxLength } from './MaxLength'; export { default as multiline } from './Multiline'; export { default as numberOfLines } from './NumberOfLines'; export { default as onSelectionChange } from './OnSelectionChange'; +export { default as onSelectionChangeControlled } from './OnSelectionChangeControlled'; export { default as placeholder } from './Placeholder'; export { default as placeholderTextColor } from './PlaceholderTextColor'; export { default as secureTextEntry } from './SecureTextEntry'; diff --git a/packages/react-native-web/src/exports/TextInput/index.js b/packages/react-native-web/src/exports/TextInput/index.js index 5cfd039f..a134eae5 100644 --- a/packages/react-native-web/src/exports/TextInput/index.js +++ b/packages/react-native-web/src/exports/TextInput/index.js @@ -22,19 +22,14 @@ import useResponderEvents from '../../hooks/useResponderEvents'; import StyleSheet from '../StyleSheet'; import TextInputState from '../../modules/TextInputState'; -const emptyObject = {}; - /** * Determines whether a 'selection' prop differs from a node's existing * selection state. */ const isSelectionStale = (node, selection) => { - if (node != null && selection != null && selection.start != null) { - const { selectionEnd, selectionStart } = node; - const { start, end } = selection; - return start !== selectionStart || end !== selectionEnd; - } - return false; + const { selectionEnd, selectionStart } = node; + const { start, end } = selection; + return start !== selectionStart || end !== selectionEnd; }; /** @@ -42,7 +37,7 @@ const isSelectionStale = (node, selection) => { * error. */ const setSelection = (node, selection) => { - if (node != null && selection != null && isSelectionStale(node, selection)) { + if (isSelectionStale(node, selection)) { const { start, end } = selection; try { node.setSelectionRange(start, end || start); @@ -155,7 +150,7 @@ const TextInput = forwardRef((props, forwardedRef) => { placeholderTextColor, returnKeyType, secureTextEntry = false, - selection = emptyObject, + selection, selectTextOnFocus, spellCheck } = props; @@ -251,7 +246,6 @@ const TextInput = forwardRef((props, forwardedRef) => { if (onChangeText) { onChangeText(text); } - handleSelectionChange(e); } function handleFocus(e) { @@ -312,22 +306,22 @@ const TextInput = forwardRef((props, forwardedRef) => { if (onSelectionChange) { try { const node = e.target; - if (isSelectionStale(node, selection)) { - const { selectionStart, selectionEnd } = node; - e.nativeEvent.selection = { - start: selectionStart, - end: selectionEnd - }; - e.nativeEvent.text = e.target.value; - onSelectionChange(e); - } + const { selectionStart, selectionEnd } = node; + e.nativeEvent.selection = { + start: selectionStart, + end: selectionEnd + }; + e.nativeEvent.text = e.target.value; + onSelectionChange(e); } catch (e) {} } } useLayoutEffect(() => { const node = hostRef.current; - setSelection(node, selection); + if (node != null && selection != null) { + setSelection(node, selection); + } if (document.activeElement === node) { TextInputState._currentlyFocusedNode = node; }