Search code examples
javascriptreact-nativereact-native-elements

I'm unable to change states on switches inside ListItem component


What i'm trying to do is to list an array with multiple items, inside this component, and this array contains a boolean state for each individual item, like this.

  items : {
    "ISA InterChile" : [
      {
        key : '7843', 
        name : "ISA APP RRHH",
        fav: false
      },
      {
        key : '7844', 
        name : "Garantia y Obligaciones",
        fav: false
      },
      {
        key : '7855', 
        name : "Que se yo",
        fav: true
      },
      {
        key : '7899', 
        name : "Random",
        fav: true
      }
    ],
    "DSAC Chile" : [
      {
        key : '5022',
        name: 'ADAM RRHH',
        fav: true
      },
      {
        key : '5023',
        name: 'Name of Project 4',
        fav: false 
      }
    ],
    "Banco BCI" : [
      {
        key : '1101',
        name: 'Name of Project 3',
        fav: false
      },
      {
        key : '1014',
        name: 'Name of Project 4',
        fav: false 
      },
      {
        key : '1170',
        name: 'Name of Project 5',
        fav: false
      },
      {
        key : '1033',
        name: 'Name of Project 6',
        fav: false 
      },
      {
        key : '1101',
        name: 'Name of Project 7',
        fav: false
      },
      {
        key : '1014',
        name: 'Name of Project 8',
        fav: false 
      },
      {
        key : '1170',
        name: 'Name of Project 9',
        fav: false
      },
      {
        key : '1033',
        name: 'Name of Project 10',
        fav: false 
      }
    ]
  },

But whenever i try to change states, it immediately changes back to its previous state, this is the code.

            {
              this.state.items[this.state.SeleccionClientes].map(value => (
                <ListItem
                  containerStyle={{backgroundColor: '#ffffff', width: widthPercentageToDP('87.1%'), height: 64, alignItems: 'center', justifyContent: 'center', alignSelf: 'center', marginTop: heightPercentageToDP('2.8%'), paddingHorizontal: 0}}
                  topDivider={false}
                  bottomDivider={true}
                  titleStyle={{
                    marginLeft: 0,
                    fontSize: 16,
                    fontWeight: "normal",
                    fontStyle: "normal",
                    lineHeight: 24,
                    letterSpacing: 0.15,
                    textAlign: "left",
                    color: "#707070"
                  }}
                  subtitleStyle={{
                    marginLeft: 0,
                    fontSize: 14,
                    fontWeight: "normal",
                    fontStyle: "normal",
                    lineHeight: 20,
                    letterSpacing: 0.25,
                    textAlign: "left",
                    color: "#c4c4c4"
                  }}
                  title={`${value.name}`}
                  subtitle={`ID ${value.key}`}
                  switch={{
                    value: value.fav, onValueChange: () => !value.fav
                  }}
                />
              ))
            }

the this.state.SeleccionClientes is from a dropdown menu, where you select the item from items, and list you the items inside

EDIT: this is the whole view and code

    import * as React from 'react';
import {View, Text, Image, StatusBar, SafeAreaView, TouchableOpacity, ScrollView, RefreshControl, FlatList} from 'react-native';
import { Header, ListItem } from 'react-native-elements';
import ModalDropdown from 'react-native-modal-dropdown';
import Images from '../../Assets/values/images';
import {
  RFPercentage as rfp,
  RFValue as rfv,
} from 'react-native-responsive-fontsize';
import {
  widthPercentageToDP as wp,
  heightPercentageToDP as hp,
  heightPercentageToDP,
  widthPercentageToDP,
} from 'react-native-responsive-screen';
import styles from '../../Assets/values/styles/HoursReport/ClientsProyects/ClientsProyectsStyle';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import DataManager from '../../../Util/CrossUtils/DataManager';

export default class ClientsProyectsScreen extends React.Component<any, any> {
  constructor(props: any) {
    super(props);

    this.state = {
      items : {
        "ISA InterChile" : [
          {
            key : '7843', 
            name : "ISA APP RRHH",
            fav: false
          },
          {
            key : '7844', 
            name : "Garantia y Obligaciones",
            fav: false
          },
          {
            key : '7855', 
            name : "Que se yo",
            fav: true
          },
          {
            key : '7899', 
            name : "Random",
            fav: true
          }
        ],
        "DSAC Chile" : [
          {
            key : '5022',
            name: 'ADAM RRHH',
            fav: true
          },
          {
            key : '5023',
            name: 'Name of Project 4',
            fav: false 
          }
        ],
        "Banco BCI" : [
          {
            key : '1101',
            name: 'Name of Project 3',
            fav: false
          },
          {
            key : '1014',
            name: 'Name of Project 4',
            fav: true 
          },
          {
            key : '1170',
            name: 'Name of Project 5',
            fav: false
          },
          {
            key : '1033',
            name: 'Name of Project 6',
            fav: false 
          },
          {
            key : '1102',
            name: 'Name of Project 7',
            fav: true
          },
          {
            key : '1114',
            name: 'Name of Project 8',
            fav: false 
          },
          {
            key : '1175',
            name: 'Name of Project 9',
            fav: false
          },
          {
            key : '1303',
            name: 'Name of Project 10',
            fav: false 
          }
        ]
      },
      SeleccionClientes: '',
    }
  }

  updateFav = item => {
    const newData = this.state.items[this.state.SeleccionClientes];
    const uodatedItem = newData.find(x => x.key === item.key);
    uodatedItem.fav = !item.fav;
    this.setState({ data: newData });
    //You can also have a callback from parent component to update parent state
  };

  render() {
    return (
      <>
        <StatusBar translucent backgroundColor="transparent" />
        <SafeAreaView style={{backgroundColor: '#fafafa'}}/>
        {/*
        <Header
          backgroundImage={Images.header_nav}
          backgroundImageStyle={styles.HeaderImagenCP}
          leftComponent={
            <TouchableOpacity onPress={() => this.props.navigation.goBack()}>
              <View><Image style={styles.HeaderHorizontal} source={Images.back}/></View>
            </TouchableOpacity>
          }
          centerComponent={{text: 'Imputar horas', ellipsizeMode: 'clip', style: styles.HeaderHoursReportCP }}
          placement='center'
        />
        */}
        <View style={styles.ContainerBackground}>
        <View style={{flexDirection: 'row'}}>
          <View style={{alignItems: 'flex-start', justifyContent: 'flex-start', alignSelf: 'flex-start', marginTop: heightPercentageToDP('2.2%'), marginLeft: widthPercentageToDP('6.3%')}}>
            <Text style={{
              fontSize: 18,
              fontWeight: "500",
              fontStyle: "normal",
              lineHeight: 35,
              letterSpacing: 0,
              textAlign: "left",
              color: "#707070"
            }}>Proyectos</Text>
          </View>
          <TouchableOpacity style={{position: 'absolute', alignItems: 'flex-end', justifyContent: 'flex-end', alignSelf: 'flex-end', paddingBottom: heightPercentageToDP('0.5%'), left: widthPercentageToDP('90%')}} onPress={() => this.props.navigation.goBack()}>
            <Image source={Images.close_modal}/>
          </TouchableOpacity>
        </View>
        <KeyboardAwareScrollView
          keyboardShouldPersistTaps="handled"
          enableOnAndroid={true}>
            {/*
            <View style={{alignItems: 'flex-end', justifyContent: 'flex-end', alignSelf: 'flex-end', marginRight: widthPercentageToDP('6.3%')}}>
              <TouchableOpacity onPress={() => this.props.navigation.goBack()}>
                <Image source={Images.close_modal}/>
              </TouchableOpacity>
            </View>
            */}
            <View style={styles.Left}>
              <Text style={styles.TituloInputOnBlur}>Cliente</Text>
            </View>
            <View style={styles.Center}>
              <ModalDropdown
                dropdownTextStyle={styles.dropdownTextStyle}
                dropdownTextHighlightStyle={styles.dropdownTextHighlightStyle}
                defaultValue={'Seleccionar'}
                style={styles.dropStyle}
                textStyle={{
                  padding: 0,
                  margin: 0,
                  fontSize: rfv(16),
                  paddingVertical: hp('1.2%'),
                  fontWeight: 'normal',
                  fontStyle: 'normal',
                  textAlign: 'left',
                  //color: Motivo != 'Seleccionar' ? '#1a1a1a' : '#c4c4c4',
                }}
                onSelect={(index, value) => this.setState({SeleccionClientes: value})}
                options={Object.keys(this.state.items)}
              />
            </View>
            <View>
            {
              this.state.SeleccionClientes !== '' ?
              <View>
                {
                  <FlatList
                    data={this.state.items[this.state.SeleccionClientes]}
                    renderItem={({item, index}) => (
                      <ListItem
                        containerStyle={{backgroundColor: '#fafafa', width: widthPercentageToDP('87.1%'), height: 64, alignItems: 'center', justifyContent: 'center', alignSelf: 'center', marginTop: heightPercentageToDP('2.8%'), paddingHorizontal: 0}}
                        topDivider={false}
                        bottomDivider={true}
                        titleStyle={{
                          marginLeft: 0,
                          fontSize: 16,
                          fontWeight: "normal",
                          fontStyle: "normal",
                          lineHeight: 24,
                          letterSpacing: 0.15,
                          textAlign: "left",
                          color: "#707070"
                        }}
                        subtitleStyle={{
                          marginLeft: 0,
                          fontSize: 14,
                          fontWeight: "normal",
                          fontStyle: "normal",
                          lineHeight: 20,
                          letterSpacing: 0.25,
                          textAlign: "left",
                          color: "#c4c4c4"
                        }}
                        title={`${item.name}`}
                        subtitle={`ID ${item.key}`}
                        switch={{
                          //trackColor: {false: "#767577", true: "#81b0ff"},
                          //thumbColor: item.fav ? "#1062cc" : "#f4f3f4",
                          value: item.fav[index], 
                          onValueChange: () => {this.updateFav(item)}
                        }}
                      />
                    )}
                  />
                }
              </View>
              :
              <View style={{alignItems: 'center', justifyContent: 'center', alignSelf: 'center'}}>
                <View style={{marginTop: heightPercentageToDP('11%')}}>
                  <Image style={{marginBottom: heightPercentageToDP('2.8%')}} source={Images.sad_face}/>
                </View>
                <Text style={{
                  fontSize: 18,
                  fontWeight: "normal",
                  fontStyle: "normal",
                  lineHeight: 35,
                  letterSpacing: 0,
                  textAlign: "left",
                  color: "#c4c4c4"
                }}>Sin proyectos activos</Text>
              </View>
            }
            </View>
          <View style={styles.BottomPush} />
        </KeyboardAwareScrollView>
        </View>
      </>
    );
  }
}

Solution

  • You are not updating the state, your switch takes the value from the state object. And better use a flat list instead of map as its good in terms of performance. This will be a sample approach that you can take.

    class CustomFlatList extends Component {
      constructor(props) {
        super(props);
    
        this.state = {
          data: this.props.data,
        };
      }
    
      updateFav = item => {
        const newData = [...this.state.data];
        const uodatedItem = newData.find(x => x.key === item.key);
        uodatedItem.fav = !item.fav;
        this.setState({ data: newData });
        //You can also have a callback from parent component to update parent state
      };
    
      render() {
        return (
          <FlatList
            data={this.state.data}
            renderItem={({ item }) => (
              <ListItem
                title={`${item.name}`}
                subtitle={`ID ${item.key}`}
                switch={{
                  value: item.fav,
                  onValueChange: () => this.updateFav(item),
                }}
              />
            )}
          />
        );
      }
    }
    

    --Updated code after full code

    You will have to update the updateFav function as below.

     updateFav = item => {
            const newData = [...this.state.items[this.state.SeleccionClientes]];
            const updateItem = newData.find(x => x.key === item.key);
            updateItem.fav = !item.fav;
    
            const updatedArray = Object.assign(this.state.items);
            updatedArray[this.state.SeleccionClientes] = newData;
            this.setState({ items: updatedArray });
        };