Skip to content

Instantly share code, notes, and snippets.

@omeileo
Created April 22, 2019 19:51
Show Gist options
  • Save omeileo/22054b4418f02491ee87ac0a4663d52c to your computer and use it in GitHub Desktop.
Save omeileo/22054b4418f02491ee87ac0a4663d52c to your computer and use it in GitHub Desktop.
Navigate to next form field onSubmitEditing (React Native)
// External Dependencies
import React, { Component } from 'react'
import { View } from 'react-native'
import PropTypes from 'prop-types'
import _ from 'lodash'
// Internal Dependencies
import FormField from './FormField/FormField.component'
import styles from './Form.styles'
export default class Form extends Component {
constructor (props) {
super(props)
this.formField = {}
}
render () {
const { formFields } = this.props
this.refs = this.formField
return (
<View style={styles.container}>
{
formFields.map((formField, index) => {
const {
autoCapitalize, autoCorrect, blurOnSubmit, enablesReturnKeyAutomatically,
fieldName, isValid, maxLength, onBlur, onChangeText, onSubmitEditing, placeholder,
returnKeyType, secureTextEntry, text, textContentType, validation
} = formField
const nextFormFieldName = (index + 1 < formFields.length) ? formFields[index + 1].fieldName : null
const _onSubmitEditing = () => {
_.isNull(nextFormFieldName)
? onSubmitEditing()
: this.refs[nextFormFieldName].textInput[nextFormFieldName].focus() // textInput object is defined in FormField
}
return (
<View style={styles.formFieldContainer} key={index}>
<FormField
autoCapitalize={autoCapitalize}
autoCorrect={autoCorrect}
blurOnSubmit={blurOnSubmit}
enablesReturnKeyAutomatically={enablesReturnKeyAutomatically}
fieldName={fieldName}
isValid={isValid}
maxLength={maxLength}
onBlur={onBlur}
onChangeText={onChangeText}
onSubmitEditing={_onSubmitEditing}
placeholder={placeholder}
returnKeyType={returnKeyType}
ref={ref => (this.formField[fieldName] = ref)}
secureTextEntry={secureTextEntry}
text={text}
textContentType={textContentType}
validation={validation}
/>
</View>
)
})
}
</View>
)
}
}
Form.propTypes = {
formData: PropTypes.arrayOf(PropTypes.shape({
autoCapitalize: PropTypes.string,
autoCorrect: PropTypes.bool,
blurOnSubmit: PropTypes.bool,
enablesReturnKeyAutomatically: PropTypes.bool,
fieldName: PropTypes.string.isRequired,
isValid: PropTypes.bool,
maxLength: PropTypes.number,
onBlur: PropTypes.func,
onChangeText: PropTypes.func,
onSubmitEditing: PropTypes.func,
placeholder: PropTypes.string,
returnKeyType: PropTypes.string,
secureTextEntry: PropTypes.bool,
text: PropTypes.string.isRequired,
textContentType: PropTypes.string,
validation: PropTypes.bool
}).isRequired)
}
// External Dependencies
import React, { Component } from 'react'
import { Image, Text, TextInput, TouchableOpacity, View } from 'react-native'
import PropTypes from 'prop-types'
import _ from 'lodash'
// Internal Dependencies
import colors from '../../../styles/colors'
import images from '../../../../assets/images/images'
import styles from './FormField.styles'
import { formFieldName } from '../../../dictionary'
export default class FormField extends Component {
constructor (props) {
super(props)
this.textInput = {}
}
state = {
isPasswordVisible: false
}
getErrorText = fieldName => {
const errorMessage = {
invalidPassword: 'Must Have At Least 8 Characters, 1 Number, & 1 Uppercase Letter.',
invalidName: `Not a Valid Name. Enter First and Last Name.`,
emailDuplicate: 'Email Already Exists',
invalidEmail: 'Not a Valid Email'
}
let text = ''
switch (fieldName) {
case formFieldName.email:
text = errorMessage.invalidEmail
break
case formFieldName.name:
text = errorMessage.invalidName
break
case formFieldName.password:
text = errorMessage.invalidPassword
break
}
return text
}
render () {
const {
autoCapitalize, autoCorrect, blurOnSubmit, enablesReturnKeyAutomatically,
fieldName, isValid, maxLength, onBlur, onChangeText, onSubmitEditing,
placeholder, returnKeyType, secureTextEntry, text, textContentType, validation
} = this.props
return (
<View>
<View style={[styles.container, secureTextEntry && { paddingRight: 35 }]}>
<TextInput
autoCapitalize={autoCapitalize}
autoCorrect={autoCorrect}
blurOnSubmit={blurOnSubmit}
enablesReturnKeyAutomatically={enablesReturnKeyAutomatically}
maxLength={maxLength}
onBlur={onBlur}
onChangeText={text => onChangeText(text)}
onSubmitEditing={onSubmitEditing}
placeholder={placeholder}
ref={ref => (this.textInput[fieldName] = ref)}
placeholderTextColor={colors.grey.D8D8D8}
returnKeyType={returnKeyType}
secureTextEntry={this.state.isPasswordVisible ? false : secureTextEntry}
style={styles.formField}
textContentType={textContentType}
underlineColorAndroid={colors.transparent}
value={text}
/>
<View style={styles.formFieldIcons}>
{
secureTextEntry &&
<TouchableOpacity
hitSlop={{ left: 10, right: 10, top: 10, bottom: 10 }}
activeOpacity={0.8}
onPress={() => this.setState({ isPasswordVisible: !this.state.isPasswordVisible })}
>
<Image
source={this.state.isPasswordVisible ? images.passwordShown : images.passwordHidden}
style={styles.passwordVisibilityIcon}
/>
</TouchableOpacity>
}
{
validation &&
<View
style={[
styles.validation,
!_.isNull(isValid) &&
{ backgroundColor: isValid ? colors.green : colors.red }
]}
/>
}
</View>
</View>
{
!_.isNull(isValid) && !isValid && validation &&
<Text style={styles.errorText}>{this.getErrorText(fieldName)}</Text>
}
</View>
)
}
}
FormField.propTypes = {
autoCapitalize: PropTypes.string,
autoCorrect: PropTypes.bool,
blurOnSubmit: PropTypes.bool,
enablesReturnKeyAutomatically: PropTypes.bool,
fieldName: PropTypes.string.isRequired,
isValid: PropTypes.bool,
maxLength: PropTypes.number,
onBlur: PropTypes.func,
onChangeText: PropTypes.func,
onSubmitEditing: PropTypes.func,
placeholder: PropTypes.string,
returnKeyType: PropTypes.string,
secureTextEntry: PropTypes.bool,
text: PropTypes.string.isRequired,
textContentType: PropTypes.string,
validation: PropTypes.bool
}
FormField.defaultProps = {
validation: false,
onBlur: () => {},
onChangeText: () => {},
placeholder: '',
text: ''
}
@omeileo
Copy link
Author

omeileo commented Apr 22, 2019

This gist shows how to dynamically navigate to the next form field in a react native form using just the inbuilt TextInput [wrapped in a View].

The takeaway is to give the FormField component itself a dynamically-generated unique ref (I used the name of the form field):
ref={ref => (this.textInput[fieldName] = ref)}

On the Form, you then want to give each FormField in the Form a ref:
ref={ref => (this.formField[fieldName] = ref)}

You then want to assign the object you stored your generated refs to the Form refs:
this.refs = this.formField

After which, you want to ascertain the next form field in the list of form fields (I passed mine as a list of objects):
const nextFormFieldName = (index + 1 < formFields.length) ? formFields[index + 1].fieldName : null

At which point you then determine if you need to go to the next field onSubmitEditing or Submit the form itself:

const _onSubmitEditing = () => {
   _.isNull(nextFormFieldName)
      ? onSubmitEditing()
      : this.refs[nextFormFieldName].textInput[nextFormFieldName].focus() // textInput object is defined in FormField
}

Finally, pass the custom _onSubmitEditing function to the TextInput:
onSubmitEditing={_onSubmitEditing}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment