Home > Blockchain >  How to get dynamic ref and focus in TextInput component on React Native
How to get dynamic ref and focus in TextInput component on React Native

Time:10-07

Hello everyone and thank you in advance.

I have a screen, where I dynamically generate TextInput (the default number of generated textInput is 4), but from the parent component you can indicate how many inputs you want to have in the view.

I have managed to dynamize the generation of inputs, but I need to dynamize the references and I can't find a way.

The way it is at the moment, it works perfect for 4 inputs, but if I create it from the parent component with 2 inputs, it breaks.

This is the code:

import React, { useRef } from 'react'
import { TextInputProps, View, TextInput } from 'react-native'

interface iPinCode extends TextInputProps {
  onComplete: (code: string) => void
  length?: number
}

const PinCode: React.FunctionComponent<iPinCode> = ({ onComplete, length }) => {
  
  const inputStyle = {
    height: 75,
    width: 50,
    fontSize: 26,
    color: '#FFF',
    backgroundColor: '#4B4B4B',
    borderRadius: 15,
    padding: 8,
    margin: 4,
  }
  
  const _getInputs = (length: number) => {
    let inputs: JSX.Element[] = []
    
    let pin: string[] = []

    let refFirstInput = useRef()
    let refSecondInput = useRef()
    let refThirdInput = useRef()
    let refFourthInput = useRef()
    
    for (let i = 0; i < length; i  ) {      
      inputs.push(
        <TextInput
          key={i}
          style={[inputStyle, { textAlign: 'center' }]}
          onChangeText={text => {
            text.length >= 1 ? pin.splice(i, 0, text) : pin.splice(i, 1)
            i === 0
              ? text.length > 0 && refSecondInput.current.focus()
              : i === 1
              ? text.length > 0 && refThirdInput.current.focus()
              : i === 2
              && text.length > 0 && refFourthInput.current.focus()
              
            console.log('PIN: ', pin)
          }}
          value={pin[i]}
          onKeyPress={({ nativeEvent }) => {
            nativeEvent.key === 'Backspace' &&
            i === 3 && refThirdInput.current.focus() ||
            i === 2 && refSecondInput.current.focus() ||
            i === 1 && refFirstInput.current.focus()
        }}
          secureTextEntry
          keyboardType="numeric"
          maxLength={1}
          returnKeyType={i === 3 ? 'done' : 'next'}
          onSubmitEditing={() => { onComplete(pin.join('')); console.log('PIN TO SEND: ', pin.join(''))}}
          ref={
            i === 0
              ? refFirstInput
              : i === 1
              ? refSecondInput
              : i === 2
              ? refThirdInput
              : i === 3
              && refFourthInput
          }
          autoFocus={i === 0 && true}
          />
      )
    }

    return (
      <View style={{ flexDirection: 'row', justifyContent: 'center' }}>
        {inputs}
      </View>
    )
  }

  return <>{_getInputs(length || 4)}</>
}

export default PinCode


Now works perfectly with 4 inputs, but breack with other number of inputs.

Need dynamic refs to pass inside for loop and use it in onChangeText and onKeyPress of TextInput component.

Thanks a lot.

CodePudding user response:

I was implements this in OTP input, first we need to create a object that have the all refs.

const inputsRef = React.useRef(inputs.map(() => React.createRef<TextInput>()));

The inputs are an array with the number of elements, in your component add this:

ref={inputsRef.current[index]}

and for access to the reference, use this :

inputsRef?.current[index]?.current?.focus();

CodePudding user response:

Soooolved!

import React, { useRef } from 'react'
import { TextInputProps, View, TextInput } from 'react-native'

interface iPinCode extends TextInputProps {
  onComplete: (code: string) => void
  length?: number
}

const PinCode: React.FunctionComponent<iPinCode> = ({ onComplete, length }) => {
  
  const inputStyle = {
    height: 75,
    width: 50,
    fontSize: 26,
    color: '#FFF',
    backgroundColor: '#4B4B4B',
    borderRadius: 15,
    padding: 8,
    margin: 4,
  }
  
  const _getInputs = (length: number) => {
    let inputs: JSX.Element[] = []
    
    let pin: string[] = []

    const mapRef: any = []
    
    for (let index = 0; index < length; index  ) {
      mapRef.push(useRef())
    }
    
    for (let i = 0; i < length; i  ) {
      inputs.push(
        <TextInput
          key={i}
          style={[inputStyle, { textAlign: 'center' }]}
          onChangeText={text => {
            text.length === 1 ? pin.splice(i, 0, text) : pin.splice(i, 1)
            i < length - 1 && text.length > 0 && mapRef[i   1].current.focus()
            text.length === 0 && i > 0 && mapRef[i].current.focus()
          }}
          value={pin[i]}
          onKeyPress={({ nativeEvent }) => {
            nativeEvent.key === 'Backspace' &&
              i > 0 && mapRef[i - 1].current.focus()
          }}
          secureTextEntry
          keyboardType="numeric"
          maxLength={1}
          returnKeyType={length - 1 ? 'done' : 'next'}
          onSubmitEditing={() => onComplete(pin.join(''))}
          ref={mapRef[i]}
          autoFocus={i === 0}
        />
      )
    }

    return (
      <View style={{ flexDirection: 'row', justifyContent: 'center' }}>
        {inputs}
      </View>
    )
  }
  return <>{_getInputs(length || 4)}</>
}

export default PinCode

  • Related