react-nativereact-hooksreact-native-flatlistreact-native-textinput

How to use the useState hook to handle multiple textInputs that are dynamic from a FlatList in React Native?



I have a functional component that is a screen and I need to handle some state. I do not want to refactor my code to make it a class component and I would like to make use of hooks. I have a component within the screen that renders a flatlist (from JSON) of a product description, quantity and a box for a user input quantity. Currently my state for all the input quantities is tied together, so when I enter a number into 1 box, that same number is displayed in all boxes. How can I seperate and store the input state using hooks?

There is a lot of examples online with using hooks to store 1 peice of data but not multiple and as I do not know how many 'products' there will be a head of time I cannot create different useState's for them. Am I best having 1 useState for all the input boxes and stored in an array?

import { Text, View, StyleSheet, SafeAreaView, FlatList } from "react-native";

function InstallScreen({ navigation, route }) {
  // State for the number installed component
  const [quantityInstalled, setQuantityInstalled] = useState([]);

  const { item } = route.params;

  // pulling just the product arrays from the job data and storing separately
  const productData = item.products;

  return (
    <View style={styles.containerNine}>
      <View style={styles.descriptionBoxContainer}>
        <View style={styles.descriptionBox}>
          <FlatList
            data={productData}
            keyExtractor={(item) => item.id.toString()}
            renderItem={({ item }) => (
              <DescriptionBox
                productDescription={item.description}
                dueQuantity={item.quantity}
                value={quantityInstalled}
                onChangeText={(value) => setQuantityInstalled(value)}
              />
            )}
          />

          <Text>Number installed is {quantityInstalled} </Text>
        </View>
      </View>
    </View>
  );
};

Description Component

import { View, Text, StyleSheet, TextInput } from "react-native";

const DescriptionBox = (props) => {
  return (
    <View style={styles.productsAllContainer}>
      <View style={styles.descriptionBoxStyle}>
        <Text style={styles.textStyle}>{props.productDescription}</Text>
      </View>

      <View style={styles.qtyBoxStyle}>
        <Text style={styles.qtyTextStyle}>{props.dueQuantity}</Text>
      </View>

      <View>
        <TextInput
          style={styles.installedBoxStyle}
          textAlign="center"
          fontSize="18"
          fontWeight="bold"
          autoCapitalize="none"
          autoCorrect={false}
          keyboardType={"numeric"}
          maxLength={5}
          value={props.value}
          onChangeText={props.onChangeText}
        />
      </View>
    </View>
  );
};

Solution

  • Disclaimer: I have not used react native, but what I usually do is make the name, the id of the item in the list then pass that name up when changing the state

    <input name={id} onChange={onChange} />
    

    then having one call to use state but make the on change function get the name from the event handler

      const [productQuantity, setProductQuantity] = React.useState('')
      const onChange = (e) => {
        const {name, value} = e.target;
        setProductQuantity({...productQuantity,[name]:value})
      };
    

    however for your specific case you'd also probably need a function to get the installed quantity from the state, you could also pass down a function to the child to do this along the lines, of

      const getProductQuantity = (id) => (
        productQuantity[id] && productQuantity[id] || 0
      )