Search code examples
react-nativeuse-statetouchableopacity

Select and unselect component when one is selected


I've to display three components (cards) from which the user can select one. I've placed those three components inside a ScrollView as:

...
            <ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
                <LocationAndPriceCard
                    price={'100'}
                    title={'Choice 3'} />
                <LocationAndPriceCard
                    price={'200'}
                    title={'Choice 2'} />
                <LocationAndPriceCard
                    price={'300'}
                    title={'Choice 1'}} />
            </ScrollView>
...

Here is how LocationAndPriceCard is coded:

...
function LocationAndPriceCard({ price, title }) {

    const [selectedLocation, setSelectedLocation] = useState("red")

    const styles = getStyles(selectedLocation);

    const selected = () => {
        if (selectedLocation == "red") {
            setSelectedLocation("green")
        } else {
            setSelectedLocation("red")
        }
    }

    return (
        <TouchableOpacity style={styles.frame} onPress={selected}>

            <Text style={styles.priceTxt}>RM {price}</Text>
            <Text style={styles.title} numberOfLines={2}>{title}</Text>
        </TouchableOpacity>
    );
}

const getStyles = (borderC) => StyleSheet.create({
    frame: {
        borderWidth: 1,
        borderColor: borderC,
        width: 180,
        backgroundColor: '#fff',
        borderRadius: 8,
        margin: 5,
        padding: 10,
    },
...

In the code above when the cad has selected it successfully change the border color to green but I can change the color of all the components. I want to make it like if one is selected all others should go back to red color.


Solution

  • Create two new props for LocationAndPriceCard, value and onPress.

    Use value to determine which card is selected and based on that change the border color.

    Use the onPress function to set the state which will have the title of the selected card, which we will be used to determine which card is selected.

    Full Working Example: Expo Snack

    enter image description here

    import React, { useState } from 'react';
    import {
      Text,
      View,
      StyleSheet,
      ScrollView,
      TouchableOpacity,
    } from 'react-native';
    import Constants from 'expo-constants';
    
    // You can import from local files
    import AssetExample from './components/AssetExample';
    
    // or any pure javascript modules available in npm
    import { Card } from 'react-native-paper';
    
    export default function App() {
      const [selected, setSelected] = useState(null);
      const handleSelected = (value) => {
        setSelected(value);
      };
      return (
        <View style={styles.container}>
          <ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
            <LocationAndPriceCard
              price={'100'}
              title={'Choice 3'}
              onPress={handleSelected}
              value={selected}
            />
            <LocationAndPriceCard
              price={'200'}
              title={'Choice 2'}
              onPress={handleSelected}
              value={selected}
            />
            <LocationAndPriceCard
              price={'300'}
              title={'Choice 1'}
              onPress={handleSelected}
              value={selected}
            />
          </ScrollView>
        </View>
      );
    }
    
    function LocationAndPriceCard({ price, title, onPress, value }) {
      return (
        <TouchableOpacity
          style={[styles.frame, { borderColor: value === title?"green":"red" }]}
          onPress={()=>onPress(title)}>
          <Text style={styles.priceTxt}>RM {price}</Text>
          <Text style={styles.title} numberOfLines={2}>
            {title}
          </Text>
        </TouchableOpacity>
      );
    }
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        paddingTop: Constants.statusBarHeight,
        backgroundColor: '#ecf0f1',
        padding: 8,
      },
      frame: {
        borderWidth: 1,
        width: 50,
        height: 50,
        backgroundColor: '#fff',
        borderRadius: 8,
        margin: 5,
        padding: 10,
      },
    });