1
votes

I encountered an issue with TextInput, which gets input from user, and a button which send the message in TextInput and clear the input. So overall flow would be:

  1. User type into TextInput
  2. At some point, user presses Button(aka. TouchableOpacity)
  3. Store the text from the TextInput to temporary buffer then clear the TextInput.
  4. Send the text via api.

Code looks like:

    {/* Message Input */}
    <TextInput
      style={styles.messageInput}
      multiline
      onChangeText={text => {
        setMessage(text);
      }}
      value={message}
    />

    {/* Send Button */}
    <Button
      title={"Send"}
      onPress={() => {
        const msg = message
        onSendMessage(msg);
        setMessage("");
      }}
      disabled={false}
      style={styles.sendButton}
    />

And my problem occurs when user type too soon after tapping the send button. If user decides to type too soon, then TextInput does not get cleared. I think this is because:

  1. User tap send => enqueues render because message state change by setMessage("") in Button's onPress
  2. User type too soon => enqueues render because message change by onChangeText handler in TextInput. The problem is setMessage from previous state hasn't been really handled yet. Therefore, this render is enqueued with message's previous value (aka. value before message was set to "" ).

I tried Promise, useEffect, and useRef, but nothing really solved this issue. If anyone knows how to overcome with this issue, please let me know. Thank you in advance.

2

2 Answers

0
votes

You should use Callback or Promise or Async/Await for this use-case. I suggest you use Promise.

onSendMessage = msg => {
    axios
      .post("/your-url", {
        msg
      })
      .then(function(response) {
        console.log(response);
        // ! You can clear the message here !
        setMessage("");
        // OR
        return new Promise(() => resolve(msg));
      })
      .catch(function(error) {
        console.log(error);
      });
  };

Something like that. The choice of using is yours :)

0
votes

useState hook is asynchronous, and will not immediately reflect and update but will trigger a re-render. Therefore you shouldn't store this value in a constant like this: const msg = message.

I'd make an async function that sends the input to the api. (Bonus: add an loading state to give the user feedback by disabling the submit button)

const [isLoading, setIsLoading] = useState(false);

onSubmit = async () => {
  setIsLoading(true);
  const response = await fetch('url/action', settings);
  if(response){
    setIsLoading(false);
    return response.json();
  }
}
    <TextInput
      style={styles.messageInput}
      multiline
      onChangeText={text => {
        setMessage(text);
      }}
      value={message}
    />

    <Button
      title={"Send"}
      onPress={() => onSubmit()}
      disabled={isLoading}
      style={styles.sendButton}
    />