Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Focusing on the next field.
  • Loading branch information
halilb committed Jan 9, 2021
1 parent dd009e2 commit af8a138
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 11 deletions.
21 changes: 19 additions & 2 deletions components/CreditCardForm.tsx
@@ -1,5 +1,5 @@
import React from 'react'
import { StyleSheet, View } from 'react-native'
import React, { useRef } from 'react'
import { Keyboard, StyleSheet, TextInput, View } from 'react-native'
import { useForm, FormProvider } from 'react-hook-form'
import cardValidator from 'card-validator'
import Button from './Button'
Expand Down Expand Up @@ -28,6 +28,11 @@ const CreditCardForm: React.FC = () => {
},
})

const holderNameRef = useRef<TextInput>(null)
const cardNumberRef = useRef<TextInput>(null)
const expirationRef = useRef<TextInput>(null)
const cvvRef = useRef<TextInput>(null)

function onSubmit(model: FormModel) {
console.log('form submitted', model)
}
Expand All @@ -37,6 +42,7 @@ const CreditCardForm: React.FC = () => {
<FormProvider {...formMethods}>
<FormTextField
style={styles.textField}
ref={holderNameRef}
name="holderName"
label="Cardholder Name"
rules={{
Expand All @@ -50,9 +56,11 @@ const CreditCardForm: React.FC = () => {
},
},
}}
onSubmitEditing={() => cardNumberRef.current?.focus()}
/>
<FormTextField
style={styles.textField}
ref={cardNumberRef}
name="cardNumber"
label="Card Number"
keyboardType="number-pad"
Expand All @@ -70,6 +78,7 @@ const CreditCardForm: React.FC = () => {
},
}}
formatter={cardNumberFormatter}
onValid={() => expirationRef.current?.focus()}
/>
<View style={styles.row}>
<FormTextField
Expand All @@ -79,8 +88,10 @@ const CreditCardForm: React.FC = () => {
marginRight: 24,
},
]}
ref={expirationRef}
name="expiration"
label="Expiration Date"
keyboardType="number-pad"
maxLength={5}
validationLength={5}
rules={{
Expand All @@ -95,9 +106,11 @@ const CreditCardForm: React.FC = () => {
},
}}
formatter={expirationDateFormatter}
onValid={() => cvvRef.current?.focus()}
/>
<FormTextField
style={styles.textField}
ref={cvvRef}
name="cvv"
label="Security Code"
keyboardType="number-pad"
Expand All @@ -118,6 +131,10 @@ const CreditCardForm: React.FC = () => {
},
},
}}
onValid={() => {
// form is completed so hide the keyboard
Keyboard.dismiss()
}}
/>
</View>
<Button
Expand Down
23 changes: 19 additions & 4 deletions components/FormTextField.tsx
@@ -1,22 +1,36 @@
import React, { useEffect } from 'react'
import { useFormContext, Controller, RegisterOptions } from 'react-hook-form'
import { TextInput } from 'react-native'
import TextField from './TextField'

type Props = React.ComponentProps<typeof TextField> & {
name: string
rules: RegisterOptions
validationLength?: number
formatter?: (oldValue: string, newValue: string) => string
onValid?: () => void
}

const FormTextField: React.FC<Props> = (props) => {
const { name, rules, validationLength = 1, formatter, ...restOfProps } = props
const FormTextField = React.forwardRef<TextInput, Props>((props, ref) => {
const {
name,
rules,
validationLength = 1,
formatter,
onValid,
...restOfProps
} = props
const { control, errors, trigger, watch } = useFormContext()
const value = watch(name)

useEffect(() => {
async function validate() {
const isValid = await trigger(name)
if (isValid) onValid?.()
}

if (value.length >= validationLength) {
trigger(name)
validate()
}
}, [value, name, validationLength, trigger])

Expand All @@ -28,6 +42,7 @@ const FormTextField: React.FC<Props> = (props) => {
// passing everything down to TextField
// to be able to support all TextInput props
{...restOfProps}
ref={ref}
errorText={errors[name]?.message}
onBlur={onBlur}
onChangeText={(text) => {
Expand All @@ -41,6 +56,6 @@ const FormTextField: React.FC<Props> = (props) => {
rules={rules}
/>
)
}
})

export default FormTextField
14 changes: 9 additions & 5 deletions components/TextField.tsx
Expand Up @@ -14,7 +14,7 @@ type Props = React.ComponentProps<typeof TextInput> & {
errorText?: string | null
}

const TextField: React.FC<Props> = (props) => {
const TextField = React.forwardRef<TextInput, Props>((props, ref) => {
const {
label,
errorText,
Expand All @@ -26,7 +26,6 @@ const TextField: React.FC<Props> = (props) => {
} = props
const [isFocused, setIsFocused] = useState(false)

const inputRef = useRef<TextInput>(null)
const focusAnim = useRef(new Animated.Value(0)).current

useEffect(() => {
Expand All @@ -52,7 +51,7 @@ const TextField: React.FC<Props> = (props) => {
borderColor: color,
},
]}
ref={inputRef}
ref={ref}
{...restOfProps}
value={value}
onBlur={(event) => {
Expand All @@ -64,7 +63,12 @@ const TextField: React.FC<Props> = (props) => {
onFocus?.(event)
}}
/>
<TouchableWithoutFeedback onPress={() => inputRef.current?.focus()}>
<TouchableWithoutFeedback
onPress={() => {
// @ts-ignore
ref?.current?.focus()
}}
>
<Animated.View
style={[
styles.labelContainer,
Expand Down Expand Up @@ -108,7 +112,7 @@ const TextField: React.FC<Props> = (props) => {
{!!errorText && <Text style={styles.error}>{errorText}</Text>}
</View>
)
}
})

const styles = StyleSheet.create({
input: {
Expand Down

0 comments on commit af8a138

Please sign in to comment.