React Native isPressed changes style for all the elements in the loop. How to change only one elements style?

Viewed 44

The below code changes style to itemStyles.listItemPressed to all the elements in the Item loop. I need to have only one element change the style on pressing the container. Down below i have my second way of tryng to do that. But in the second try the pressed styles apply and stay there after i let go of my finger. I need the style to change back to default after i dont press on the screen.

const [isPressed, setIsPressed] = React.useState(false);

const [selectBtn, setSelectBtn] = React.useState(null);
    
function onPressIn() {
    setIsPressed(true);
}

function onPressOut() {
    setIsPressed(false);
}

 <View>
                    {options.map((option) => (
                        <Pressable
                            onPress={onPress}
                            onPressIn={onPressIn}
                            onPressOut={onPressOut}
                            style={[
                                itemStyles.listItem,
                                isPressed && itemStyles.listItemPressed,
                            ]}
                        >
                            <Item
                                checked={option.value === value}
                                key={option.value}
                                label={option.label}
                                disabled={disabled}
                                onPress={() => onChange(option.value)}
                            />
                        </Pressable>
                    ))}
                </View>

Second try

       return (
            <View style={style}>
                <View>
                    {options.map((option, index) => (
                        <Pressable
                            style={[
                                itemStyles.listItem,
                                { backgroundColor: selectBtn === index ? 'red' : 'gray' },
                            ]}
                            onPressIn={() => {
                                setSelectBtn(index);
                            }}
                            onPressOut={null}
                            onPress={onPress}
                        >
                            <Item
                                checked={option.value === value}
                                key={option.value}
                                label={option.label}
                                disabled={disabled}
                                onPress={() => onChange(option.value)}
                            />
                        </Pressable>
                    ))}
                </View>
            </View>
        );
    }

My options array

 options={[
                    { label: "Value", value: "value" },
                    { label: "Value", value: "value2" },
                    { label: "Value", value: "value3" },
                    { label: "Value", value: "value4" },
                    { label: "Value", value: "value5" },
                ]}
3 Answers

Im assuming you know which elements index you want to change style when onPressed

Hey you can do something like this : UPDATED LINK https://snack.expo.dev/uRUZuvibJ check out this

import {React,useState} from 'react';
import { Text, View, StyleSheet,Pressable } 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';

const data = ["hey","india","anime","manga","arsenal"]

export default function App() {

  const [selected,setSelect] = useState([])

  const handlePress = (index) => {
    let existingState = [...selected];
    if(existingState.includes(index)){
      existingState = existingState.filter(item => item !== index)
      setSelect(existingState)
    } else {
      existingState.push(index)
      setSelect(existingState)
    }
  }

  const renderItem = (item,index) => {
    const isSelected = selected.includes(index)
    return(
      <Pressable style={isSelected?styles.press:styles.unpress}  onPressIn={() => handlePress(index)} onPressOut={() => handlePress(index)} >
      <Text>{item}</Text>
      </Pressable>
   )
  }

  return (
    <View style={styles.container}>
     {data.map((item,index) => {
       return renderItem(item,index)
     })}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
  paragraph: {
    margin: 24,
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
   unpress:{
     height:40,
     width:'100%',
     backgroundColor:'red',
     flex:1
   },
   press:{
     height:40,
     width:'100%',
     backgroundColor:'green',
     flex:1
   }
});
Hope it helps :)

enter image description here

You could use some other logic for this.

But what have done with a similar problem where. I had to change color of an element in a loop on hover.

I made a separate component for the item inside the loop on which i had to apply changes.

Then in that separate component i had a state like isPressed. Now what it does is that each element in the loop have its own personal state for isPressed so i can check it for each individual item without affecting other elements in the loop.

For Example: App.js

function App(){
  return <div>
  {someArray.map((item)=><Component ...props... />)}
</div>
}

Component.js

function Component(){
const [isPressed, setIsPressed] = useState(false);
return 
 <someTag onClick={()=>setIsPressed(true)}  style={{color:isPressed ? 'blue' : 'red'}} >....</somTag>

}

You can also handle function on click in App.js then you have to pass function as a prop to Component and onClick in Component it will call props.sentFunction ...

I'd approach this problem that have elements with multiple properties and states by using useReducer: https://snack.expo.dev/@zvona/state-of-multiple

Here's the full code from the snack of handling state through useReducer:

import { useState, useReducer } from 'react';
import { Text, Pressable, View, StyleSheet } from 'react-native';

const options = [{ value: 'first' }, { value: 'second' }, { value: 'third' }];
const initialState = { ...options };

const App = () => {
  const selectedReducer = (state, { type, index }) => {
    const { selected, pressed } = state[index];
    const newState = { ...state };

    switch (type) {
      case 'pressIn':
        newState[index].pressed = true;
        break;
      case 'pressOut':
        newState[index].pressed = false;
        break;
      case 'press':
        newState[index].selected = !selected;
        break;
      default:
        console.warn('invalid type');
        break;
    }

    return newState;
  };

  const [state, dispatch] = useReducer(selectedReducer, initialState);

  const getStyling = (index) => [
    styles.pressableStyle,
    state[index].selected && styles.listItemSelected,
    state[index].pressed && styles.listItemPressed,
  ];

  return (
    <View style={styles.container}>
      <View>
        {options.map((option, index) => (
          <Pressable
            onPressIn={() => dispatch({ type: 'pressIn', index })}
            onPressOut={() => dispatch({ type: 'pressOut', index })}
            onPress={() => dispatch({ type: 'press', index })}
            style={getStyling(index)}>
            <Text>{option.value}</Text>
          </Pressable>
        ))}
      </View>{' '}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#ecf0f1',
    padding: 8,
  },

  pressableStyle: {
    backgroundColor: 'white',
    padding: 10,
    marginBottom: 10,
    borderColor: 'black',
    borderWidth: 1,
  },

  listItemPressed: {
    backgroundColor: 'blue',
  },

  listItemSelected: {
    backgroundColor: 'green',
  },
});

export default App;
Related