问题:React-Native - TextInput 的每次击键时键盘都会关闭

Posted

技术标签:

【中文标题】问题:React-Native - TextInput 的每次击键时键盘都会关闭【英文标题】:Issue: React-Native - Keyboard closes on each keystroke for TextInput 【发布时间】:2020-08-25 06:46:33 【问题描述】:

对此的完全免责声明 - 我已经使用 react native 大约一两个星期了,我怀疑我遇到了这个问题而没有完全理解为什么!

问题:在 TextInput 字段中的每次击键时,键盘都会自动关闭并且只记录第一次击键。

情况:我使用预定义数组作为 useState 的默认值。基于当前状态使用 .map() 调用 TextInput 字段。 onChangeText() 更新状态以捕获对数组的更改。每次击键都会更新状态。

尝试过的事情:

    为 .map() 中使用的不同组件添加/删除 Key 将 keyboardShouldPersistTaps='handled' 添加到调用 .map() 的 ScrollView,包括所有其他可用的变体

有谁知道是什么导致键盘在每次击键时关闭,以及如何在继续捕获主状态下 TextInput 字段的更改的同时防止这种情况发生?

下面是我正在处理的代码片段(我已经删除了一些不相关的细节):

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 

【问题讨论】:

【参考方案1】:

将 onChangeText 替换为 onEndEditing

onEndEditing=(bidAmts) => setBidAmt(bidAmts)

代替

onChangeText=(bidAmts) => setBidAmt(bidAmts)

【讨论】:

【参考方案2】:

在我使用 FieldArray redux-form 的情况下。

我为 TextInput 添加了超时。

const [displayText, setDisplayText] = useState(input.value && input.value.toString());

const timerRef = useRef(null);

const handleChange = text => 
    setDisplayText(text);

    if (isDelayOnChangeNeeded) 
      if (timerRef.current) 
        clearTimeout(timerRef.current);
      
      timerRef.current = setTimeout(() => 
        input && input.onChange(text);
      , 1500);
     else 
      input && input.onChange(text);
    
  ;

【讨论】:

【参考方案3】:

对此的一种解决方案是使用 Input 上的 onEndEditing 属性。您可以将本地状态与 onChange 属性一起使用,然后一旦用户点击离开/点击完成,就会调用 onEndEditing 函数,您可以将本地状态应用于父状态。


 const TextInput = ( value, onChange ) => 
      const [currentValue, setCurrentValue] = useState(`$value`);
      return (
          <Input
            value=currentValue
            onChangeText=v => setCurrentValue(v)
            onEndEditing=() => onChange(currentValue)
          />
      );
 ;

这样,父 onChange 属性只会在字段更新完成后调用,而不是在每次击键时调用。除非您正在做一些花哨的事情,否则这很好。

【讨论】:

【参考方案4】:

由于这里的主要问题是键盘关闭,这里有一个简单的解决方案。将属性 autoFocus=true 添加到您的 TextInput。每当页面刷新时,这将自动关注输入字段。

【讨论】:

这对多个字段有一些有趣的行为 - 我注意到击键时焦点在输入之间跳跃,即使键盘保持打开状态,焦点也不总是保持 是的,因为您可以将自动对焦保持在特定的输入字段而不是每个输入字段。 使用此解决方案时,键盘仍会消失。再次出现,但对最终用户来说并没有带来很好的可用性【参考方案5】:

由于您正在更改列表,因此您的所有输入都将重新呈现。避免这种情况的一种方法是将当前编辑文本存储到另一个状态值中,并在提交输入或失去焦点后将其合并到列表中。这是最小的例子:

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

【讨论】:

谢谢。这是一种享受。我也很感谢你让这一切变得容易理解!

以上是关于问题:React-Native - TextInput 的每次击键时键盘都会关闭的主要内容,如果未能解决你的问题,请参考以下文章

onChange 仅在 2 个文本输入后触发

更新来自其他组件的道具反应原生

react-native init 指定 react 版本和 react-native 版本

React-Native + crypto:如何在 React-Native 中生成 HMAC?

修改 react-native 文件

使用 react-native run-android 运行时出现 React-Native 错误