Rabbit
Rabbit

Reputation: 117

React Native dynamic styles, adding styles to one element

I'm trying to add style to an element, but the style gets on all elements. How do I get style to just the clicked element and not all of them?

I want the element TouchableOpacity to add the style of listItemsDynmaicOpen when its clicked, witch is does but the problem is that its adding the style to all of my TouchableOpacity elements.

So how do i do to just get style on the pressed TouchableOpacity?

Btw im a total newbie when it comes to React and React-Native

import React, { Component } from 'react';
import { View, StyleSheet, Text, TouchableOpacity } from 'react-native';
import ListItemHeart from './ListItemHeart/ListItemHeart';
class ListItems extends Component {
state = {
    ActiveDances: [
      {"name":"Agneta & Peter","month":"Jan","day":27,"weekday":"Söndag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Feb","day":23,"weekday":"Fredag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Apr","day":3,"weekday":"Måndag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
    ],
    listClicked: false
};
_showListItem = () => {
    if(this.state.listClicked) {
        this.setState({
            listClicked: false
        });
    }else{
        this.setState({
            listClicked: true
        });
    }
}

render() {
    const activedance = this.state.ActiveDances.map((ActiveDance, i) => {
      return (
        <TouchableOpacity key={i} style={this.state.listClicked ? styles.listItemsDynamicOpen : styles.listItemsDynamicClosed} onPress={this._showListItem}>
                <View style={styles.listItemsDate}>
                    <Text style={styles.listItemsDaytext}>{ActiveDance.day}</Text>
                    <Text style={styles.listItemsBoldtext}>{ActiveDance.month}</Text>
                </View>
                <View style={styles.listItemsInfo}>
                    <Text style={styles.nametext}>{ActiveDance.name}</Text>
                    <View style={styles.listItemsInfoDayTime}>
                        <Text style={styles.normaltext}>{ActiveDance.weekday}</Text>
                        <Text style={styles.normaltext}>{ActiveDance.time}</Text>
                    </View>
                    <Text style={styles.normaltext}>{ActiveDance.place}</Text>
                    <Text style={styles.normaltext}>{ActiveDance.city}</Text>
                </View>
                <View style={styles.heart}>
                    <ListItemHeart />
                </View>
        </TouchableOpacity>
        );
    });
    return (
        <View style={styles.listItems}>{activedance}</View>
        );
}

}
const styles = StyleSheet.create({
listItems: {
    width: "100%",
    backgroundColor:"#f2f2f2",
    paddingTop:15
},
listItemsDynamicClosed: {
    width:"100%",
    height:110,
    marginBottom: 10,
    flexDirection: 'row',
    backgroundColor: "white",
},
listItemsDynamicOpen: {
    width: "100%",
    height: 200,
    marginBottom: 10,
    flexDirection: "row",
    backgroundColor: "white"
},
listItemsDate: {
    width:"15%",
    height: 50,
    alignItems:"center",
    marginTop:10
},
heart: {
    width:"10%",
    alignItems:"center",
    justifyContent:"center"
},
listItemsInfo: {
    marginLeft:10,
    width:"65%"
},
listItemsInfoDayTime: {
    width:"60%",
    flexDirection:'row',
    justifyContent:'space-between'
},
listItemsDaytext:{
    fontSize: 20,
    color:"#39B54A"
},
listItemsBoldtext: {
    fontSize: 19
},
nametext:{
    paddingTop:5,
    paddingBottom:5,
    fontSize:19,
    color:"#808080"
},
normaltext: {
    fontSize: 16,
    paddingTop:1,
    paddingBottom:1,
    color:"#4d4d4d"
}

});
export default ListItems;

Upvotes: 1

Views: 1698

Answers (3)

Kevva
Kevva

Reputation: 83

You need to differentiate between different touchables. You should not use array index for this ( as your "key" prop is currently ), it would be great if it was some unique id. But in this case, I can show how to do it with the array index for simplicity.

Your state:

state = { ActiveDances: "your list", selectedDance: null }

On dance select:

_showListItem = (index) => {
  if(this.state.selectedDance === index) { // if the dance is selected, unselect it
    this.setState({
       selectedDance: null
    });
  } else { // if the dance is not selected, select it
    this.setState({
       selectedDance: index
    });
  }
}

Your touchable:

<TouchableOpacity
   key={i} 
   style={this.state.selectedDance === i ? styles.listItemsDynamicOpen : 
   styles.listItemsDynamicClosed}
   onPress={() => this._showListItem(i)}>

Upvotes: 1

ShocKwav3_
ShocKwav3_

Reputation: 1760

Your state for tracking the click listClicked is getting shared through all the TouchableOpacity items. If we try to simulate what happens when this.state.ActiveDances.map executes, it returns all the ActiveDances items like:

<TouchableOpacity key=0 style={styles.listItemsDynamicClosed> </TouchableOpacity>
<TouchableOpacity key=1 style={styles.listItemsDynamicClosed> </TouchableOpacity>
<TouchableOpacity key=2 style={styles.listItemsDynamicClosed> </TouchableOpacity>
......

Notice here styles.listItemsDynamicClosed is because of this expression this.state.listClicked ? styles.listItemsDynamicOpen : styles.listItemsDynamicClosed} since this.state.listClicked is still false as you have not clicked yet.

Now when you click and this.state.listClicked is true and since all the TouchableOpacity are sharing it all of them renders as:

<TouchableOpacity key=0 style={styles.listItemsDynamicOpen> </TouchableOpacity>
<TouchableOpacity key=1 style={styles.listItemsDynamicOpen> </TouchableOpacity>
<TouchableOpacity key=2 style={styles.listItemsDynamicOpen> </TouchableOpacity>
......

This is where breaking it into small component comes into play. Imagine if you have one component which is only returning one TouchableOpacity then you could've listClicked applied in only one TouchableOpacity and only the clicked one would update.

//ClickableItem.js
class ClickableItem extends Component {
  state = {
    listClicked = false,
  }

  _showListItem = () => {
    this.setState({
      listClicked: !this.state.listClicked
    })

  render() {
    return(
      <TouchableOpacity style={this.state.listClicked ? syles.listItemsDynamicOpen : styles.listItemsDynamicClosed} onPress={this._showListItem}>
        ...
      </TouchableOpacity>
   )

  }
}

export default ClickableItem

And then in the main/parent component:

//List.js
import ClickableItem from './ClickableItem.js'
class List extends Component {
state = {
    ActiveDances: [
      {"name":"Agneta & Peter","month":"Jan","day":27,"weekday":"Söndag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Feb","day":23,"weekday":"Fredag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Apr","day":3,"weekday":"Måndag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
    ]
}

  render() {
    return(
     this.state.ActiveDances.map((ActiveDance, i) => <ClickableItem key={i}>)
   )

  }
}

Anything data that you need to pass to the children components (ClickableItem in this case) you can pass it down as props and receive it through this.props. More info: https://reactjs.org/docs/components-and-props.html

And since you are new take a look on good practices in javascript codes. One thing I'd like to point out: use camelCase when naming variable references. For react capital case on the beginning of a name should be used for tag/component names only.

I hope it helps you. Welcome to React and React Native :)

Upvotes: 1

Saeid
Saeid

Reputation: 2036

Well, you can use this trick. You can use TouchableHighlight:

import React, { Component } from 'react';
import { View, StyleSheet, Text, TouchableOpacity, TouchableHighlight } from 'react-native';
import ListItemHeart from './ListItemHeart/ListItemHeart';
class ListItems extends Component {
state = {
    ActiveDances: [
      {"name":"Agneta & Peter","month":"Jan","day":27,"weekday":"Söndag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Feb","day":23,"weekday":"Fredag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Apr","day":3,"weekday":"Måndag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
      {"name":"Agneta & Peter","month":"Aug","day":1,"weekday":"Lördag","place":"Kulturens hus Bollnäs","time":"18.00-21.30","city":"Bollnäs"},
    ],
     pressStatus:false,
};


_onHideUnderlay() {
    this.setState({ onPressStatus: false });
}
_onShowUnderlay() {
    this.setState({ pressStatus: true });
}

render() {
    const activedance = this.state.ActiveDances.map((ActiveDance, i) => {
      return (
         <TouchableHighlight
                activeOpacity={1}
                style={
                    this.state.pressStatus
                        ? styles.listClicked
                        : styles.listItemsDynamicOpen
                }
                onHideUnderlay={this._onHideUnderlay.bind(this)}
                onShowUnderlay={this._onShowUnderlay.bind(this)}

            >
                <View style={styles.listItemsDate}>
                    <Text style={styles.listItemsDaytext}>{ActiveDance.day}</Text>
                    <Text style={styles.listItemsBoldtext}>{ActiveDance.month}</Text>
                </View>
                <View style={styles.listItemsInfo}>
                    <Text style={styles.nametext}>{ActiveDance.name}</Text>
                    <View style={styles.listItemsInfoDayTime}>
                        <Text style={styles.normaltext}>{ActiveDance.weekday}</Text>
                        <Text style={styles.normaltext}>{ActiveDance.time}</Text>
                    </View>
                    <Text style={styles.normaltext}>{ActiveDance.place}</Text>
                    <Text style={styles.normaltext}>{ActiveDance.city}</Text>
                </View>
                <View style={styles.heart}>
                    <ListItemHeart />
                </View>
        </TouchableHighlight >
        );
    });
    return (
        <View style={styles.listItems}>{activedance}</View>
        );
}

}

Upvotes: 0

Related Questions