onSubmitEditing: Error: Maximum update depth exceeded - react-native

There is a textinput in my screen. Besides that there is a search icon. Upon pressing the search icon, this.searchmethod() is called and searching is performed. I also want to perform searching upon pressing the "search", "done", "go" or "enter" button of the keyboard. I tried onSubmitEditing like shown in below, which resulted in the error:
Error: Maximum update depth exceeded
My code:
<TextInput
onChangeText = {
text => {
this.setState({
searchtag: text
})
}
}
onSubmitEditing={this.searchmethod()}
onFocus = {() => {
if (this.state.searchtag != "")
this.setState({
searchtag: ''
}, () => {
this._refreshData()
})
}
}
/>
<TouchableWithoutFeedback
onPress = {
() => {
this.searchmethod()
}
} >
<View>
<FearIcon name = "search" size = {25}/>
</View>
</TouchableWithoutFeedback>
Kindly guide me how I can perform searching upon pressing "done" or etc key on Keyboard. Thanks in advance.

Try doing this
onSubmitEditing={() => this.searchmethod()}
Instead of onFocus, refreshData in onChangeText like
this.setState({
searchtag: text
},
() => this._refreshData();
)

Related

react-native-gifted-chat How to send on pressing return

How do I make the return button on the mobile keyboard send the message instead of creating a new line? I tried using onSubmitEditing in the textInputProps but couldn't get it to work.
You need to implement your own ChatComposer and pass the onSubmitEditing prop in the textInputProps in there. In order to prevent keyboard dismiss you also need to set blurOnSubmit to false.
const [messages, setMessages] = useState([])
const onSend = useCallback((messages = []) => {
setMessages((previousMessages) => GiftedChat.append(previousMessages, messages))
}, [])
const ChatComposer = (
props: ComposerProps & {
onSend: SendProps<IMessage>["onSend"]
text: SendProps<IMessage>["text"]
}
) => {
return (
<Composer
{...props}
textInputProps={{
...props.textInputProps,
blurOnSubmit: false,
multiline: false,
onSubmitEditing: () => {
if (props.text && props.onSend) {
props.onSend({ text: props.text.trim() }, true)
}
},
}}
/>
)
}
return (
<GiftedChat messages={messages} onSend={onSend} renderComposer={ChatComposer} />
)
If you want to remove the default send button from the text input field on the right, you need to pass a custom renderSend button, which could be empty, e.g.
renderSend={() => {}}
Notice, that I have tested all of the above on iOS only. Android might behave differently.

Why does header button onPress act differently than regular button onPress?

I have a method called sendResults() that makes an API call and does some array manipulation. When I call the method using a "normal" TouchableOpacity button, everything works fine. However, when I call it using a button I have placed in the App Stack header, the method does not run correctly. It feels like an async issue (?) but not sure...
Here is the code for the two buttons.
<TouchableOpacity
onPress={() => {
sendResults(); // works fine
}}
style={styles.buttonStyle}
>
<Text>Save</Text>
</TouchableOpacity>
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity
onPress={() => {
sendResults(); // doesn't work
}}
style={styles.buttonStyle}
>
<Text>Save</Text>
</TouchableOpacity>
),
});
}, []);
Edit: sendResults() code
// Shows alert confirming user wants to send results to API
const sendResults = () => {
Alert.alert("Save Results", "Alert", [
{
text: "Save & Quit",
onPress: () => postNumsAndNavigate(),
style: "destructive",
},
{ text: "Cancel", onPress: () => console.log("") },
]);
};
// Save Results button
const postNumsAndNavigate = async () => {
if (bibNums.length == 0) {
alert("You have not recorded any results. Please try again.");
} else if (bibNums.filter((entry) => entry == "").length > 0) {
alert("Blank");
} else {
console.log("\n" + bibNums);
await postNums();
AsyncStorage.setItem(`done`, "true");
navigation.navigate("Home Screen");
}
};
postNums() does an API call.
Edit 2: bibNums declaration
const [bibNums, setBibNums] = useState([]);
You set the handler of the navigation button only once because your useEffect doesn't have any dependency; it runs only when the component is mounted, it captures an old reference of sendResults. sendResults changes every time postNumsAndNavigate and bibNums change. Add sendResults to the dependency array to update the navigation button handler every time sendResults changes.
useEffect(() => {
...
}, [sendResults])
It works correctly for the TouchableOpacity because you are assigning the handler on every render.
onPress={() => {sendResults()}}

react-native reseting TextInput after ClearTextOnFocus

I am building a form in React-Native and have set ClearTextOnFocus to true as it is easier to handle dynamic formating for editing.
I am trying to add a reset function by setting all local state to the redux store, but if the user has not typed anything in a selected TextInput, the local state has not changed, and react native does not re-render the TextInput; leaving it blank.
Anyone have any thoughts on how I can unclear the TextInput or force React to re-render. Code is a work in progress, but here are the relevant bits.
Thanks
class GoalScreen extends Component {
componentWillMount = () => this.setPropsToState();
onReset = () => {
this.setPropsToState();
}
onChange = text => this.setState({ [text.field]: text.input });
setPropsToState = () => {
const { name } = this.props.goal;
this.setState({ name });
};
render() {
const { name } = this.state;
return (
<View style={styles.screenContainer}>
<Text style={styles.text}> Name </Text>
<TextInput
placeholder="a brand new bag"
keyboardType="default"
autoCorrect={false}
style={styles.inputField}
clearTextOnFocus
onChangeText={text => this.onChange({ input: text, field: 'rate' })}
value={name}
/>
</View>
}
}
So, I'm not using Redux, and my use case might be a bit different than yours, but I thought my solution might still be relevant here, if only to confirm that (after hours of wrangling with this) it appears that passing true to the clearTextOnFocus prop prevents further updates to a TextInput component.
I tried every conceivable workaround (like setNativeProps(), forceUpdate()) but nothing worked, so I ended up having to basically write my own logic for clearing and resetting the input text.
This component should 1) clear input text on focus and then 2) reset it to its previous value if the user hasn't pressed a key:
class ResettableInput extends Component {
state = {
Current: this.props.value,
Previous: ""
};
KeyPressed = false;
//cache current input value for later revert if necessary, and clear input
onFocus = () => {
this.setState({ Previous: this.state.Current, Current: "" });
};
//record whether key was pressed so input value can be reverted if necessary
onKeyPress = () => {
this.KeyPressed = true;
};
onChangeText = text => {
this.setState({ Current: text });
};
//if no key was pressed, revert input to previous value
onBlur = () => {
if (!this.KeyPressed) {
this.setState({ Current: this.state.Previous, Previous: "" });
}
};
render = () => {
return (
<TextInput
onChangeText={this.onChangeText}
value={this.state.Current}
onBlur={this.onBlur}
onFocus={this.onFocus}
onKeyPress={this.onKeyPress}
/>
);
};
}

React Native TextInput: no newline with hardware keyboard Enter key

We have a React Native TextInput component in our app. Using the virtual keyboard, pressing enter creates a new line. If we use a hardware keyboard (attached to an Android 6 tablet using an USB OTG adapter), the Enter key (the large one in the middle of the keyboard) doesn't change the text, the TextInput only loses the focus. The Return key (the smaller one on the right side of common keyboards) creates a new line.
The TextInput is setup very basic:
<TextInput multiline={true} />
I tried different values for the returnKeyType attribute, but none of them created a new line. Am I missing something?
You're welcome: blurOnSubmit={true}
I was facing the same issue, but the following worked for me:
returnKeyType='none'
<TextInput
value={activity}
onChangeText={setActivity}
numberOfLines={5}
multiline={true}
style={styles.TextInput}
placeholder={"Start your activity"}
keyboardType="name-phone-pad"
/>
This works for me
Try it! It works in the middle of the line also!
<TextInput
placeholder={I18n.t('enterContactQuery')}
value={this.state.query}
onChangeText={text => this.setState({ query: text, allowEditing: true })}
selection = {this.state.selection}
onSelectionChange={(event) => this.setState({ cursorPosition: event.nativeEvent.selection, selection: event.nativeEvent.selection, allowEditing: true })}
onSubmitEditing={() => {
const { query, cursorPosition } = this.state;
let newText = query;
const ar = newText.split('');
ar.splice(cursorPosition.start, 0, '\n');
newText = ar.join('');
if (cursorPosition.start === query.length && query.endsWith('\n')) {
this.setState({ query: newText });
} else if (this.state.allowEditing) {
this.setState({
query: newText,
selection: {
start: cursorPosition.start + 1,
end: cursorPosition.end + 1
},
allowEditing: !this.state.allowEditing
});
}
}}
multiline = {true}
numberOfLines = {10}
blurOnSubmit={false}
editable={true}
// clearButtonMode="while-editing"
/>
constructor(props) {
super(props);
this.state = {
query: '',
cursorPosition: [0,0],
selection: null,
allowEditing: true
}
}

Detecting send/submit button in a multi-line TextInput

The React Native TextInput component doesn't support the onSubmitEditing event if it specified as a multi-line input.
Is there a way to detect when the user presses the enter/submit/send (depending on which keyboard layout is specified) button after entering some text?
I realize this is an old post, but I stumbled here from Google and wanted to share my solution. Because of some things that needed to happen in the case of submit, vs simply blurring, I wasn't able to use onBlur to interpret submit.
I utilized an onKeyPress listener to track the Enter key, and then proceeded with the submit. (Note, this is currently only supported in iOS until this PR is merged.)
// handler
onKeyPress = ({ nativeEvent }) => {
if (nativeEvent.key === 'Enter') {
// submit code
}
};
// component
<TextInput
autoFocus={true}
blurOnSubmit={true}
enablesReturnKeyAutomatically={true}
multiline={true}
onChangeText={this.onChangeText}
onKeyPress={this.onKeyPress}
returnKeyType='done'
value={this.props.name}
/>
Note, the blurOnSubmit is still required to prevent the return key from being passed to your onChangeText handler.
On iOS this should work according to the documentation.
Use the onBlur function:
Callback that is called when the text input is blurred
In combination with the ios only blurOnSubmit:
If true, the text field will blur when submitted. The default value is
true for single-line fields and false for multiline fields. Note that
for multiline fields, setting blurOnSubmit to true means that pressing
return will blur the field and trigger the onSubmitEditing event
instead of inserting a newline into the field.
I'll try testing this.
Edit:
Done testing
blurOnSubmit doesn't work like it's supposed to in react-native 0.14.2. Even when set to true, the return/done button and enter key just create a newline and do not blur the field.
I'm not able to test this on newer versions at the moment.
Try it! It works in the middle of the line also!
<TextInput
placeholder={I18n.t('enterContactQuery')}
value={this.state.query}
onChangeText={text => this.setState({ query: text, allowEditing: true })}
selection = {this.state.selection}
onSelectionChange={(event) => this.setState({ cursorPosition: event.nativeEvent.selection, selection: event.nativeEvent.selection, allowEditing: true })}
onSubmitEditing={() => {
const { query, cursorPosition } = this.state;
let newText = query;
const ar = newText.split('');
ar.splice(cursorPosition.start, 0, '\n');
newText = ar.join('');
if (cursorPosition.start === query.length && query.endsWith('\n')) {
this.setState({ query: newText });
} else if (this.state.allowEditing) {
this.setState({
query: newText,
selection: {
start: cursorPosition.start + 1,
end: cursorPosition.end + 1
},
allowEditing: !this.state.allowEditing
});
}
}}
multiline = {true}
numberOfLines = {10}
blurOnSubmit={false}
editable={true}
// clearButtonMode="while-editing"
/>
constructor(props) {
super(props);
this.state = {
query: '',
cursorPosition: [0,0],
selection: null,
allowEditing: true
}
}
constructor () {
super()
this.state = {
text : '',
lastText : '',
inputHeight:40
}
}
writing(text){
this.setState({
text : text
})
}
contentSizeChange(event){
if(this.state.lastText.split("\n").length != this.state.text.split("\n").length){
this.submitTextInput();
}else{
this.setState({
inputHeight : event.nativeEvent.contentSize.height
})
}
}
submitTextInput(){
Alert.alert('submit input : ' + this.state.text);
this.setState({
text : ''
})
}
render() {
return (
<View style={{flex:1,backgroundColor:'#fff'}}>
<TextInput
style={{height:this.state.inputHeight}}
multiline={true}
onChangeText={(text) => this.writing(text)}
onContentSizeChange={(event) => this.contentSizeChange(event)}
onSubmitEditing={() => this.submitTextInput()}
value={this.state.text}
/>
</View>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>