DavidB
DavidB

Reputation: 37

useState Array boolean toggle - React Native

Beginner developer react native.

im dealing with design pattern issue , i have multiple TouchableOpacity's in the same component (i have to keep it that way). for each one i have onPress function thats changs there background and reverse . the problom is that the function dependent on State current statment and when i click on one of them evreyone is changing .



function Grocery({ navigation }) {

  const [isPressed, setIsPressed] = useState(0);
  const onPress = () => setIsPressed(!isPressed);



  return (  
    
    <ScrollView>
      <Button title="home" onPress={() => {FindMatch(GetIngridients());navigation.navigate("MatchedRecipiesScreen");}}>press</Button>
    <View style={styles.container}>
      
    <TouchableOpacity style={styles.button} onPress={() => {AddToPanetry("pasta");onPress();}}  >
    <View style={isPressed && styles.pressedButtonStyle} />
        <Image style={styles.imageright} source={require('../assets/Pastaa.jpg')} />
        <Text> pasta</Text>
      </TouchableOpacity>



      <TouchableOpacity onPress={() => {AddToPanetry("eggs");onPress();}}  >
      <View style={isPressed && styles.pressedButtonStyle} />
        <Image style={styles.imageleft} source={require('../assets/eggs.jpg')} />
        <Text>eggs</Text>
      </TouchableOpacity>




      <TouchableOpacity onPress={() => {AddToPanetry("fish");onPress();}}  >
      <View style={isPressed && styles.pressedButtonStyle} />
        <Image style={styles.imageleft} source={require('../assets/fish.jpg')} />
        <Text>fish</Text>
      </TouchableOpacity>


const styles = StyleSheet.create({
  container: {
    flexDirection: "row",
    flexWrap: "wrap",
    padding: 50,
    flexWrap: 'wrap',
    justifyContent: 'space-between',
  }
  ,
  imageleft: {
    borderRadius:100,
    borderWidth:2,
    borderColor:'black',
    height: 120,
    width: 150,
    borderRadius: 80,
    padding:25
  },
  button: {
    alignItems: "center",
   
  },
    tinyLogo: {
    width: 50,
    height: 50,
  },
  pressedButtonStyle: {
    position:"absolute",
    width:150,
    height:121,
    backgroundColor:'black',
    opacity:0.6,
    zIndex:100,
    borderRadius:80
  },
  imageright: {
    borderRadius:100,
    borderWidth:2,
    borderColor:'black',
    height: 120,
    width: 150,
    borderRadius: 80,
    padding:25
  }
});

Upvotes: 2

Views: 1373

Answers (2)

ksav
ksav

Reputation: 20821

There are 2 options depending on your needs.

  1. You could keep your all of your data and selected state in a single stateful array. On press, you need to find the item in the array that will update.
export default function Grocery({ navigation }) {
  const [state, setState] = React.useState([ { label: 'pasta', pressed: false, }, { label: 'eggs', pressed: false, }, { label: 'fish', pressed: false, }, { label: 'salad', pressed: false, }, ]);

  const handlePress = (i) => {
    const newState = [...state];
    newState[i].pressed = !state[i].pressed;
    setState(newState);
  };

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <FlatList
        data={state}
        ListHeaderComponent={() => (
          <Button
            title="home"
            onPress={() => {console.log('press home')}}>
            press
          </Button>
        )}
        renderItem={({ item, index }) => (
          <TouchableOpacity
            key={index}
            id={index}
            onPress={() => handlePress(index)}
            style={[
              styles.flatListTouchable,
              item.pressed && styles.flatListTouchablePressed,
            ]}>
            <Text style={styles.flatListTouchableText}>{item.label}</Text>
          </TouchableOpacity>
        )}
      />
    </SafeAreaView>
  );
}

Snack


  1. You could keep your data and selected state separately. The selected state is managed in the child component.
const data = [ { label: 'pasta', }, { label: 'eggs', }, { label: 'fish', }, { label: 'salad', }, ];

export default function Grocery({ navigation }) {
  return (
    <SafeAreaView style={{ flex: 1 }}>
      <FlatList
        data={data}
        ListHeaderComponent={() => (
          <Button
            title="home"
            onPress={() => {
              console.log('press home');
            }}>
            press
          </Button>
        )}
        renderItem={({ item, index }) => (
          <RenderItem item={item} index={index} />
        )}
      />
    </SafeAreaView>
  );
}

const RenderItem = ({ item, index }) => {
  const [pressed, setPressed] = React.useState(false);

  const handlePress = () => {
    setPressed(!pressed);
  };

  return (
    <TouchableOpacity
      key={index}
      id={index}
      onPress={handlePress}
      style={[
        styles.flatListTouchable,
        pressed && styles.flatListTouchablePressed,
      ]}>
      <Text style={styles.flatListTouchableText}>{item.label}</Text>
    </TouchableOpacity>
  );
};

Snack

Upvotes: 1

Stepan Nikulenko
Stepan Nikulenko

Reputation: 653

One of the approaches is to store item names in an array or object and then check if the particular item were selected.

Here is another approach you could use:

const HomeScreen = () => {
    const itemsData = [
        { name: 'Eggs', image: 'image require here', isSelected: false },
        { name: 'Pasta', image: '', isSelected: false },
        { name: 'Fish', image: '', isSelected: false },
    ];

    const [items, setItems] = useState(itemsData);

    const handleSelectItem = (selectedItemIndex) => {
        const itemsToSelect = items.map((item, index) => {
        if (selectedItemIndex === index) item.isSelected = !item.isSelected;
        return item;
        }, []);

        setItems(itemsToSelect);

        // your logic here
        // AddToPanetry(item[selectedItemIndex].name)
    };

    const renderItem = (item, index) => {
        const isSelected = items[index].isSelected;

        return (
        <TouchableOpacity
            style={[styles.button, isSelected && styles.selectedButton]}
            onPress={() => handleSelectItem(index)}>
            {/* <Image source={item.image} /> */}
            <Text>{item.name}</Text>
        </TouchableOpacity>
        );
    };

    return (
        <View>
        <ScrollView>
            {itemsData.map((item, index) => renderItem(item, index))}
        </ScrollView>
        </View>
    );
};

const styles = StyleSheet.create({
  button: {
    backgroundColor: 'white',
    padding: 20,
  },
  selectedButton: {
    backgroundColor: 'pink',
  },
});

Upvotes: 4

Related Questions