React Native TextInput: no newline with hardware keyboard Enter key - react-native

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
}
}

Related

onSubmitEditing: Error: Maximum update depth exceeded

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();
)

React Native Form Validation

I created a login form using react-native and I want to validate every fields but I don't know how to do it. I'm quite new to react-native so I want to ask anyone for help. Form validation should show error under following conditions:
Input form is empty
Email text isn't email form.
Password text does not satisfy the conditions above.
If Input form has errors the login button should be disabled.
If Input form doesn't have any errors, show alert to inform login
success
Sample image validation:
Here is my code:
import React from 'react';
import { StyleSheet, Text, View, Image, TextInput, Dimensions, ScrollView,
CheckBox, TouchableOpacity } from 'react-native';
import logo from './image/Logo.png'
const { width: WIDTH } = Dimensions.get('window')
export default class App extends React.Component {
constructor(){
super();
this.state={
check:false,
email: '',
};
this.validates = this.validates.bind(this);
}
CheckBoxText(){
this.setState({
check:!this.state.check,
})
}
validates = () => {
let text = this.state.email;
let emailError = this.state.emails;
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/ ;
if(reg.test(text) === false)
{
console.warn("Invalid email")
this.setState({email:text})
return false;
}
else {
this.setState({email:text})
console.log("Email is Correct");
}
}
render() {
return (
<View>
<View style={styles.container}>
<Image source={logo} style={styles.logo}/>
</View>
<View style = {styles.container2}>
<Text style={styles.emailAdd}>
Email
</Text>
<TextInput
onChangeText={(text) => this.setState({email:text})}
type='email'
value={this.state.email}
keyboardType='email-address'
style={styles.emailInput}
placeholder={'Input Email Address'}
underlineColorAndroid='transparent'/>
</View>
<View style = {styles.container3}>
<Text style={styles.password}>
Password
</Text>
<TextInput
style={styles.passwordInput}
placeholder={'Input Password'}
secureTextEntry={true}
underlineColorAndroid='transparent'/>
</View>
<View style = {styles.container4}>
<View>
<CheckBox value={this.state.check} onChange={()=>this.CheckBoxText()} style={styles.rememberMe}/>
</View>
<View>
<Text style={styles.remember}>Remember me</Text>
</View>
</View>
<TouchableOpacity style={styles.btnLogin} onPress={this.validates} >
<Text style={styles.txtLogin}>Sign In</Text>
</TouchableOpacity>
</View>
);
}
}
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
var validRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
if (!email) {
Toast.show('Email is required.');
} else if (!email.match(validRegex)) {
Toast.show('Invalid Email');
} else if (!password) {
Toast.show('Password is required.');
}
I recommend using formik and yup to easily build a login form with input validation. These two packages when integrated together, simplifies your codebase thanks to both of its features.
Please take a look at a CodeSandbox snippet here, https://codesandbox.io/s/stack-overflow-54204827-llvkzc?file=/index.tsx:254-656. And note, I'm using typescript here.
The package.json file at the time of written snippet is:
"dependencies": {
...
"formik": "2.2.9",
...
"yup": "0.32.11"
},
And to break the solution down, first we define our yup schema for our Login form:
Note, you may tweak the regex pattern later, as this password validation accepts min 6 to max 12 characters, with at least one uppercase letter, one lowercase letter, one number and one special character.
/**
* The `yup` Login Form schema
*/
const LoginSchemaA = Yup.object().shape({
email: Yup.string()
.email("Invalid email.")
.required("Email must be provided."),
password: Yup.string()
.required("Password must be provided.")
.matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##\$%\^&\*])(?=.{6,12})/,
"Password must be minimum 6 and maximum 12 characters."
)
});
Note, .email("Invalid email.") here is the default email validation feature used. You can remove this, and use .matches(...) function instead for your own regular expression.
And just the <Formik /> section for your further use:
<Formik
initialValues={{
email: "",
password: ""
}}
validationSchema={LoginSchemaA}
onSubmit={(
values: Values,
{ setSubmitting }: FormikHelpers<Values>
) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 500);
}}
>
{({ errors, touched }) => (
<Form>
<label htmlFor="email">Email</label>
<Field
id="email"
name="email"
placeholder="john.doe#email.com"
type="email"
/>
{errors.email && touched.email ? (
<div style={{ color: "red" }}>{errors.email}</div>
) : null}
<label htmlFor="password">Password</label>
<Field id="password" name="password" type="password" />
{errors.password && touched.password ? (
<div style={{ color: "red" }}>{errors.password}</div>
) : null}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
Lastly, you would want to grab the values itself for further use - ignore the setTimeout, alert and setSubmitting usages.
An example output of JSON.stringify(values, null, 2) would be as below:
{
"email": "john.doe#test.com",
"password": "Awesome#Password!2022"
}
Hope this helps you in your react-native coding journey!
here is my code you can try this
import React, { Component } from "react"
import { View, Button } from "react-native"
import TextField from "textfield"
import validation from "validation"
import validate from "validation_wrapper"
export default class Form extends Component {
constructor(props) {
super(props)
this.state = {
email: "",
emailError: "",
password: "",
passwordError: ""
}
}
register() {
const emailError = validate("email", this.state.email)
const passwordError = validate("password", this.state.password)
this.setState({
emailError: emailError,
passwordError: passwordError
})
if (!emailError && !passwordError) {
alert("Details are valid!")
}
}
render() {
return (
<View>
<TextField
onChangeText={(value) => this.setState({ email: value.trim() })}
onBlur={() => {
this.setState({
emailError: validate("email", this.state.email)
})
}}
error={this.state.emailError}
/>
<TextField
onChangeText={(value) => this.setState({ password: value.trim() })}
onBlur={() => {
this.setState({
passwordError: validate("password", this.state.password)
})
}}
error={this.state.passwordError}
secureTextEntry={true}
/>
<Button title="Register" onPress={this.validateRegister} />
</View>
)
}
}
<!-- begin snippet: js hide: false console: true babel: false -->
const validation = {
email: {
presence: {
message: "^Please enter an email address"
},
email: {
message: "^Please enter a valid email address"
}
},
password: {
presence: {
message: "^Please enter a password"
},
length: {
minimum: 5,
message: "^Your password must be at least 5 characters"
}
}
}
export default validation
import validation from "validation.js"
export default function validate(fieldName, value) {
// Validate.js validates your values as an object
// e.g. var form = {email: 'email#example.com'}
// Line 8-9 creates an object based on the field name and field value
var formValues = {}
formValues[fieldName] = value
// Line 13-14 creates an temporary form with the validation fields
// e.g. var formFields = {
// email: {
// presence: {
// message: 'Email is blank'
// }
// }
var formFields = {}
formFields[fieldName] = validation[field]
// The formValues and validated against the formFields
// the variable result hold the error messages of the field
const result = validatejs(formValues, formFields)
// If there is an error message, return it!
if (result) {
// Return only the field error message if there are multiple
return result[field][0]
}
return null
}
import React from "react"
import { View, TextInput, Text } from "react-native"
const TextField = (props) => (
<View>
<TextInput />
props.error ? <Text>{props.error}</Text> : null
</View>
)
export default TextField

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 to limit lines in TextInput

If set multiline as true, React Native will accept unlimited lines in TextInput, there has maxLength but it just limits the maximum number of characters, so how to set maximum lines on TextInput?
You can use maxHeight and minHeight to accept what you want. For standard text fontSize, giving maxHeight={60} will make TextInput scrollable after 3 lines. This is good for IOS - for Android you can use numberOfLines prop.
I created a simple version to extend the TextInput to give the maxLines feature.
import React, { Component } from "react";
import { TextInput } from "react-native";
import PropTypes from "prop-types";
export default class MultiLine extends Component {
static propTypes = {
maxLines: PropTypes.number
};
constructor(props) {
super(props);
this.state = {
value: props.value || ""
};
}
onChangeText = text => {
const { maxLines, onChangeText } = this.props;
const lines = text.split("\n");
if (lines.length <= (maxLines || 1)) {
onChangeText(text);
this.setState({ value: text });
}
};
render() {
const { onChangeText, multiline, value, ...other } = this.props;
return (
<TextInput
{...other}
multiline={true}
value={this.state.value}
onChangeText={this.onChangeText}
/>
);
}
}
A example:
<Multiline
value={this.state.testeText}
maxLines={10}
onChangeText={text => {
this.setState({ testeText: text });
}}
/>
I have implemented the following component for an app I'm the owner. It is tested only on a restricted environment, so it probably has to be adapted to your need. I am sharing here with the hope it helps the community.
react-native: 0.46.3
import React, { Component } from 'react'
import { TextInput, Platform } from 'react-native'
const iosTextHeight = 20.5
const androidTextHeight = 20.5
const textHeight = Platform.OS === 'ios' ? iosTextHeight : androidTextHeight
class TextInputLines extends Component {
state = { height: textHeight * 2, lines: 1 }
componentWillReceiveProps (nextProps) {
if (nextProps.value === '') {
this.setState({ height: textHeight * 2, lines: 1 })
}
}
render () {
const {style, value, placeholder, numberOfLines = 8, autoFocus = true, onChangeText} = this.props
return (
<TextInput
style={[style, { height: this.state.height }]}
multiline
autoFocus={autoFocus}
value={value}
placeholder={placeholder}
onChangeText={(text) => onChangeText(text)}
onContentSizeChange={(event) => {
const height = Platform.OS === 'ios'
? event.nativeEvent.contentSize.height
: event.nativeEvent.contentSize.height - androidTextHeight
const lines = Math.round(height / textHeight)
const visibleLines = lines < numberOfLines ? lines : numberOfLines
const visibleHeight = textHeight * (visibleLines + 1)
this.setState({ height: visibleHeight, lines: visibleLines })
}}
underlineColorAndroid='transparent'
/>
)
}
}
export default TextInputLines
How to use:
import TextInputLines from './TextInputLines'
Within your render() method:
<TextInputLines
style={textStyle}
autoFocus={true}
value={this.state.value}
placeholder={textPlaceholder}
onChangeText={onChangeText}
numberOfLines={10}
/>
I don't think you can on iOS. As mentioned, Android has the prop numberOfLines. The closest I can think of would be to try to estimate the width of your input and limit the characters based on that dimension but it's my opinion that it may be a dark path to go down.
Should you choose to do so, someone was trying to get the number of lines here. A combination of the onLayout function to get the width of your TextInput and onChangeText to limit it might work.
It seems no props or packages to quickly implement checking number of lines, so I use javascript function match to get the number from TextInput, then if the number reach the max value, do something to limit it.
numberOfLines doesn't nothing in android too
You can do something like this
In Component state
this.state = {height: 35};
In JSX TextInput declaration
style={[styles.default, {height: Math.min(Math.max(35, this.state.height),120)}]}
I solved the limit issue using maxHeight,numberOfLine and multiline prop
<TextInput placeholder="description here" numberOfLines={3} maxHeight={60} onChangeText={t => setDescription(t)} multiline={true} style={{ margin: 10, borderRadius: 20, padding: 5, borderWidth: 1 }} />
I was solving this same issue. Here's what I came up with.
You can either remove the last trailing newline, or all of them. Both work differently, so use accordingly to how you want yours to function. I personally chose the remove last trailing newline alternative:
function onChangeText(text) {
if (props.multiline) {
const newlines = (text.match(/\n/g) || '').length + 1;
if (newlines > MAX_LINES) {
props.setState(text.replace(/\n$/, "")); // Remove last trailing newline
// props.setState(text.replace(/\n+$/, "")); // Remove all trailing newlines
return;
}
}
props.setState(text);
return;
};

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>