Home > Software engineering >  useState Array boolean toggle - React Native
useState Array boolean toggle - React Native

Time:10-21

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
  }
});

CodePudding user response:

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',
  },
});

CodePudding user response:

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 should update.
    import * as React from 'react';
    import {
      Text,
      StyleSheet,
      TouchableOpacity,
      Button,
      FlatList,
      SafeAreaView,
    } from 'react-native';

    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>
      );
    }

    const styles = StyleSheet.create({
      flatListTouchable: {
        width: '100%',
        backgroundColor: 'blue',
        color: 'white',
        padding: 30,
      },
      flatListTouchablePressed: {
        backgroundColor: 'black',
      },
      flatListTouchableText: {
        color: 'white'
      }
    });

Snack


  1. You could keep your data and selected state separately. Selected state is managed in the child component.
    import * as React from 'react';
    import {
      Text,
      StyleSheet,
      TouchableOpacity,
      Button,
      FlatList,
      SafeAreaView,
    } from 'react-native';

    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>
      );
    };

    const styles = StyleSheet.create({
      flatListTouchable: {
        width: '100%',
        backgroundColor: 'blue',
        color: 'white',
        padding: 30,
      },
      flatListTouchablePressed: {
        backgroundColor: 'black',
      },
      flatListTouchableText: {
        color: 'white',
      },
    });

Snack

  • Related