'react native disable text input until previous input filled in

What im trying to achieve is this, when a user gets to the form; I want the first input to be active, so the user is able to click it and fill it out. And they shouldn't be able to move to the next text field input until the first one has been filled.

I also want the submit button to be deactivated and not to be activated until all the inputs are filled out.

What's the best way of achieving this?

Below code is our form. also added a screenshot of our screen to give you a visual reference.

 <Icon name="numeric-2-circle" color="#777777" size={40} />
            <Text style={styles.textEnterMsg}>Enter a message</Text>
          </View>
          <View style={styles.inputMsg}>
            <Center>
              <Input
                variant="outline"
                placeholder="Recipient's name"
              />
              <Input
                variant="outline"
                placeholder="Add message"
                maxWidth="300px"
              />
            </Center>
          </View>
          <View
            style={{
              flexDirection: "row",
              justifyContent: "space-between",
              alignItems: "center",
            }}
          >
            <Icon name="numeric-3-circle" color="#777777" size={40} />
            <Text style={styles.textGiftMsg}>Send gift</Text>
          </View>
          <View
            style={{
              flexDirection: "row",
              justifyContent: "space-between",
              alignItems: "center",
            }}
          >
            <RadioButtonRN
              style={styles.RN}
              box={false}
              boxStyle={styles.box}
              textStyle={styles.text}
              data={data}
              selectedBtn={(e) => console.log(e)}
            />
          </View>
          <Card style={styles.Card}>
            <Card.Content>
              <Title
                style={{
                  color: "white",
                  fontSize: "15",
                }}
              >
                Where would you like to send the gift?
              </Title>
              <Text style={{ color: "white"}}>We’ll text your recipient with your gift and message</Text>
              <Center>
                <View style={styles.inputContainer}>
                  <TextInput
                    placeholder="Recipient's Phone Number"
                    value={number}
                    onChangeText={(text) => setNumber(text)}
                    style={styles.input}
                  />
                </View>
              </Center>
            </Card.Content>
          </Card>
        </ScrollView>

for the submit button:

  <TouchableOpacity
          style={{
            backgroundColor: "#023531",
            alignItems: "center",
            justifyContent: "center",
          }}
          onPress={() => {
            setModalVisible(false);
            navigation.navigate("ShopList");
          }} 
        >
          <Text
            style={{
              color: "white",
              fontSize: 18,
            }}
          >
            Send Gift →
          </Text>
        </TouchableOpacity>


Solution 1:[1]

The Input component of react-native inherits TextInput, but its state is handled internally which makes it perfectly valid to write

<Input
   variant="outline"
   placeholder="Recipient's name"
/>

However, in your usecase it might be advised to handle this on your own since the states of some of your input fields are dependent on others.

Since, we inherit from TextInput we can handle this as follows.

const [recipientName, setRecipientName] = useState("")

...

<Input
  variant="outline"
  placeholder="Recipient's name"
  value={recipientName}
  onChangeText={setRecipientName}
/>

In order for the next field to be disabled until the user inputs something into the recipient field, we can use the state recipientName as follows.

const [message, setMessage] = useState("")

<Input
   variant="outline"
   placeholder="Add message"
   maxWidth="300px"
   value={message}
   onChangeText={setRecipientName}
   editable={recipientName && recipientName.trim().length > 0}
/>

The important piece of code is given by

editable={recipientName && recipientName.trim().length > 0}

We first check if recipientName is undefined (which should actually not happen in our code but let us be defensive here) and then we use trim to prevent that just whitespaces are valid and finally check that its length is greater than zero. If this criteria match, then the Input field becomes editable.

We can do the same for the other fields. It might be a good idea to define a function for this as follows.

const isMessageEditable = useCallback(() => {
    return recipientName && recipientName.trim().length > 0
}, [recipientName])

The above would allow us to add additional conditions in an isolated function. Let us imagine that we want the name to be at least 3 characters long, then we could implement this as well. For fields which are dependent by more than one field, this is easier to handle as well.

For disabling the button component, this is pretty much the same pattern, but the prop is called disabled.

<TouchableOpacity
    disabled={some conditions here as above}
    style={{
        backgroundColor: "#023531",
        alignItems: "center",
        justifyContent: "center",
     }}
     onPress={() => {
        setModalVisible(false);
        navigation.navigate("ShopList");
      }}>
      <Text
         style={{
           color: "white",
           fontSize: 18,
         }}>
            Send Gift ?
      </Text>
</TouchableOpacity>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 David Scholz