Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue: React-Native - Keyboard closes on each keystroke for TextInput

Full disclaimer upfront for this one - I've been working with react native for around a week or two, and I suspect that I've encountered this issue without fully understanding why!

Issue: On each keystroke within a TextInput field, the keyboard closes automatically and only records the first keystroke.

Situation: I am using a prefefined array as the default for useState. The TextInput fields are called using .map() based on the current state. The onChangeText() updates the sate to capture changes to the array. The state is updated with each keystroke.

Things Tried:

  1. Adding/removing Key to different components used in .map()
  2. Adding keyboardShouldPersistTaps='handled' to the ScrollView that the .map() is called in, including all other variations avaialble

Does anyone know what is causing the keyboard to close on each keystroke, and how I can prevent this from happening whilst continuing to capture changes to the TextInput fields in the main state?

Snippet below of the code I'm working on (I've removed some of the unrelated detail):

import React, { useState } from 'react';
import {
  View,
  Text,
  Button,
  TextInput,
  SectionList,
  SafeAreaView,
  TouchableOpacity,
  ScrollView,
  Modal,
} from 'react-native';
import { Picker} from '@react-native-community/picker';



//import custom components

import { styles, Break } from './MasterStyles';
import { inputData, ingredients } from './inputData';



function addNewLoaf() {

  const [ingredientsList, setIngredientsList] = useState(ingredients);
  const [selectedLoaf, setSelectedLoaf] = useState('Regular Loaf');
  const [flourModalVisibility, setFlourModalVisibility] = useState(false);
  const [newLoaf, setNewLoaf] = useState('');

  function IngredientsRecorder() {

    return (
      <View style={styles.ingredientsContainer}>
        <View style={{flexDirection: 'column'}}>
          <View>
            <Text style={styles.metricTitle}>
              Volume of Ingredients:
            </Text>
          </View>
          {
            ingredientsList.map(e => {
              if(e.isVisible && e.ingredient){
                return (
                  <View style={{flexDirection: 'row', alignItems: 'center'}} key={e.id}>
                    <View style={{flex:2}}>
                      <Text style={styles.metricText}>{e.name}:</Text>
                    </View>
                    <View style={{flex:3}}>
                      <TextInput
                        placeholder='amount'
                        style={styles.inputText}
                        keyboardType='number-pad'
                        value={e.amount}
                        onChangeText={value => ingredientsAmountHandler(value, e.id)}
                      />
                    </View>
                    <View style={{flex:1}}>
                      <Text style={styles.ingredientsText}>{e.units}</Text>
                    </View>
                  </View>
                )
              }
            })
          }
        </View>
      </View>
    )
  }



  const ingredientsAmountHandler = (text, id) => {
    // setAmount(enteredText);

    let newArray = [...ingredientsList]
    let index = newArray.findIndex(element => element.id === id)

    newArray[index].amount = text
    setIngredientsList(newArray)
  }


  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.page}>
        <Text style={styles.titleText}>Add a New Loaf</Text>
        <Break />
        <View style={{flexDirection: 'row'}}>
          <TextInput 
            placeholder='What would you like to call your loaf?' 
            style={styles.inputText}
            onChangeText={loafNameInputHandler}
            value={newLoaf}
          />
          <Button title='Create Loaf' color='#342e29' onPress={addNewLoafHandler} />
        </View>
        <Break />
        <ScrollView styles={styles.page} keyboardShouldPersistTaps='handled'>
          <LoafSelector />
          <FlourSelector />
          <IngredientsRecorder />
        </ScrollView>
      </View>
      <Break />
    </SafeAreaView>
  );
}

  export { addNewLoaf }
like image 537
Jamie T Avatar asked May 09 '20 17:05

Jamie T


People also ask

How to disable keyboard on drag in React Native ScrollView?

Luckily, React Native team has a fix for this. The keyboardDismissMode property can be set inside the ScrollView. Setting it to on-drag, ensures that the keyboard is dismissed when a drag begins. The simulator below shows this property being used.

Does react-native support onkeypreime?

react-native#19096: Doesn't support Android's onKeyPreIme. react-native#19366: Calling .focus () after closing Android's keyboard via back button doesn't bring keyboard up again. react-native#26799: Doesn't support Android's secureTextEntry when keyboardType="email-address" or keyboardType="phone-pad".

What are some common problems with React-Native?

react-native#19366: Calling .focus () after closing Android's keyboard via back button doesn't bring keyboard up again. react-native#26799: Doesn't support Android's secureTextEntry when keyboardType="email-address" or keyboardType="phone-pad".

Why do I have to tap the keyboard twice when typing?

When the keyboard is up, tapping anywhere else on the page will require you to do it twice. The first time you tap anywhere outside the TextInput, it dismisses the keyboard. Once the keyboard is dismissed the second tap enables the action. This can be seen in the example below.


1 Answers

Since you are changing the list all of your inputs are getting re-rendered. One way to avoid this would be storing your current editing text into another state value and merge it to the list after the input is submitted or lost focus. Here is the minimal example:

let defaultTemp={editingIndex:-1,text:''}

let [temp,setTemp] = useState(defaultTemp); //We will store current being edited input's data and index

{
        ingredientsList.map((e,i) => {
          if(e.isVisible && e.ingredient){
            return (
              <View style={{flexDirection: 'row', alignItems: 'center'}} key={e.id}>
                <View style={{flex:2}}>
                  <Text style={styles.metricText}>{e.name}:</Text>
                </View>
                <View style={{flex:3}}>
                  <TextInput
                    placeholder='amount'
                    style={styles.inputText}
                    keyboardType='number-pad'
                    value={temp.editingIndex===i?temp.text:e.amount}
                    //the input got focus
                    onFocus={()=>setTemp({editingIndex:i,text:e.amount})}
                    //the input lost focus
                    onBlur={()=>{
                         ingredientsAmountHandler(temp.text, e.id)
                         setTemp(defaultTemp)
                    }
                    onChangeText={text => setTemp({text,editingIndex:i})}
                  />
                </View>
                <View style={{flex:1}}>
                  <Text style={styles.ingredientsText}>{e.units}</Text>
                </View>
              </View>
            )
          }
        })
      }
like image 192
OriHero Avatar answered Sep 20 '22 13:09

OriHero