2

I'm creating a typing race game with React Native which is producing an unexpected bug on Android.

When the user finishes typing a word/spacebar, I set the input state to an empty string.

However, if the user finishes typing a word/spacebar then immediately presses the first letter of the next word/spacebar, the onChangeText event returns the state which existed prior to setting it to a empty string, along with the new letter. It should instead return the new letter by itself.

The bug only occurs when typing very quickly on Android (emulator and real device), IOS hasn't been tested, and the web app works fine typing at any speed.

Snack to try it yourself with complete code: https://snack.expo.dev/@ariato/typing-race-bug-example?platform=android

const Race = () => {
  const [input, setInput] = useState("");
  const [wordIndex, setWordIndex] = useState(0);
  const [letterIndex, setLetterIndex] = useState(0);
  const [errorLength, setErrorLength] = useState(0);

  const totalIndex = getTotalIndex(wordIndex, letterIndex);

  const onChange = (val: string) => {
    setInput(val);
    checkInput(val);
  };

  const checkInput = (val: string) => {
    const inputLength: number = val.length;
    const answer = wordList[wordIndex].substring(0, inputLength);

    if (val === answer) {
      setErrorLength(0);
      if (val === wordList[wordIndex]) {
        const isLastWord = wordIndex === wordList.length - 1;
        if (isLastWord) {
          setWordIndex(0);
        } else {
          setWordIndex((wordIndex) => wordIndex + 1);
        }
        setLetterIndex(0);
        setInput("");
      } else {
        setLetterIndex(inputLength);
      }
    } else {
      setErrorLength(inputLength - letterIndex);
    }
  };

  return (
    <KeyboardAvoidingView
      style={styles.container}
      behavior={Platform.OS === "ios" ? "padding" : "height"}
    >
      <View style={styles.textContainer}>
        {wordList.map((word, wordIdx) => (
          <Text key={wordIdx}>
            {word.split("").map((letter, letterIdx) => (
              <Text key={letterIdx} style={getStyles(wordIdx, letterIdx)}>
                {letter}
              </Text>
            ))}
          </Text>
        ))}
      </View>
      <TextInput
        value={input}
        onChangeText={onChange}
        autoCapitalize="none"
        autoComplete="off"
        autoCorrect={false}
        spellCheck={false}
        importantForAutofill="no"
        autoFocus
        keyboardType="visible-password"
        style={styles.textInput}
      />
    </KeyboardAvoidingView>
  );
};

export default Race;

This screenshot is after typing It', and then typing s and a spacebar in rapid succession. The next character to type is underlined. For each incorrect character in the input there is a red letter.

Race screen displaying bug

These logs show how the input state is set to an empty string, then becomes It's_ in one key stroke, as though the internal state of the TextInput did not get changed to an empty string.

Logs of event and state values

I tried:

  • Moving the first setInput() in the onChange() to the if/else statements in checkInput() so that it would only trigger once per event, in case something strange was happening with setState() batching. No change, as expected.
Aria
  • 33
  • 5
  • Hello, I have shown the code it's very complex logic you do. --> when you are entering the **It's** word then you need to clear the textinput and then you need to enter the **whitespace** again you need to clear the textinput and then again stuff for the same. – Ankit Vora Dec 15 '22 at 07:05
  • @AnkitVora I do clear the state value. You can see in the logs I posted that once the word "It's" is produced by the event, I set input to an empty string. The TextInput does not seem to reflect that on the next key input though. – Aria Dec 16 '22 at 02:46

0 Answers0