1

I am a bit new to react native and I am having difficulty updating object Array State and screen style on button item click during run time in react native

I tried using useState but the style does not change at run time on the touchable item click

Please advise me on the right way to change the screens view style on item click

see my code below

import React, {useState} from 'react';
import {ScrollView, FlatList, View, Text, TouchableOpacity} from 'react-native';

import styles from './styles';

export default function Loans({}) {
  const [selectedDurationId, setSelectedDurationId] = useState(1);
  const [selectedAmountId, setSelectedAmountId] = useState(1);

  const changeSelectedDuration = function (id) {
    console.log('before selected Duration');
    console.log(id);
    //change all the selecteds to no
    durationArray.forEach(function (arrayItem) {
      var x = (arrayItem.selected = 'no');
      console.log(x);
      setSelectedDurationId(id);
      updateDataArray(durationArray, selectedDurationId);
      console.log('after selected Duration');
    });
  };

  const changeSelectedAmount = function (id) {
    console.log('before selected Amount');
    console.log(id);
    //change all the selecteds to no
    loanAmountArray.forEach(function (arrayItem) {
      var x = (arrayItem.selected = 'no');
      console.log(x);
    });
    setSelectedAmountId(id);
    updateDataArray(loanAmountArray, selectedAmountId);
    console.log('after selected Amount');
  };

  const updateDataArray = function (array, id) {
    array.forEach(function () {
      //change selected to yes where id == id
      if (array.id === id) {
        var x = (array.selected = 'yes');
        console.log(x);
      }
    });
  };

  const loanAmountArray = [
    {
      id: 1,
      amount: '5,000',
      selected: 'yes',
    },
    {
      id: 2,
      amount: '10,000',
      selected: 'no',
    },
    {
      id: 3,
      amount: '20,000',
      selected: 'no',
    },
  ];

  const durationArray = [
    {
      id: 1,
      days: '30 days',
      rate: '3.3% Interest',
      selected: 'yes',
    },
    {
      id: 2,
      days: '60 days',
      rate: '5% Interest',
      selected: 'no',
    },
    {
      id: 3,
      days: '90 days',
      rate: '7% Interest',
      selected: 'no',
    },
  ];

  return (
    <View style={{flex: 1}}>
      <ScrollView>
        <View style={styles.contain}>
          <Text>Chose Loan Amount</Text>
          <FlatList
            numColumns={6}
            data={loanAmountArray}
            keyExtractor={(item, index) => {
              console.log('index', index);
              return index.toString();
            }}
            renderItem={({item}) => {
              console.log('item', item);
              return (
                <View>
                  <TouchableOpacity
                    onPress={() => {
                      changeSelectedAmount(item.id);
                    }}>
                    <Text
                      style={
                        item.selected === 'yes'
                          ? styles.textBoxSelected
                          : styles.textBox
                      }>
                      {item.amount}
                    </Text>
                  </TouchableOpacity>
                </View>
              );
            }}
          />

          <Text>Chose Payment Duration</Text>
          <FlatList
            numColumns={3}
            data={durationArray}
            keyExtractor={(item, index) => {
              console.log('index', index);
              return index.toString();
            }}
            renderItem={({item}) => {
              console.log('item', item);
              return (
                <View>
                  <TouchableOpacity
                    style={
                      item.selected === 'yes'
                        ? styles.durationViewPressed
                        : styles.durationView
                    }
                    onPress={() => {
                      changeSelectedDuration(item.id);
                    }}>
                    <View>
                      <Text style={styles.interest}>{item.rate}</Text>
                    </View>
                    <View>
                      <Text style={styles.days}>{item.days}</Text>
                    </View>
                  </TouchableOpacity>
                </View>
              );
            }}
          />
        </View>
      </ScrollView>
    </View>
  );
}

here's the style below

import {StyleSheet} from 'react-native';

export default StyleSheet.create({
  textBox: {
    marginTop: 13,
    marginBottom: 30,
    paddingTop: 10,
    paddingLeft: 16,
    paddingRight: 6,
    fontSize: 18,
    borderColor: '#1a2856',
    borderWidth: 5,
    borderTopLeftRadius: 20,
    borderTopRightRadius: 20,
    borderBottomLeftRadius: 20,
    borderBottomRightRadius: 20,
  },
  textBoxSelected: {
    marginTop: 13,
    marginBottom: 30,
    paddingTop: 10,
    paddingLeft: 16,
    paddingRight: 6,
    fontSize: 18,
    backgroundColor: '#1a2856',
    color: '#fff',
    borderWidth: 5,
    borderTopLeftRadius: 20,
    borderTopRightRadius: 20,
    borderBottomLeftRadius: 20,
    borderBottomRightRadius: 20,
  },
  durationView: {
    marginTop: 10,
    marginBottom: 20,
    paddingLeft: 5,
    paddingRight: 5,
  },
  durationViewPressed: {
    marginTop: 10,
    marginBottom: 20,
    paddingLeft: 5,
    paddingRight: 5,
    borderColor: '#1a2856',
    borderWidth: 5,
  },
  interest: {
    color: '#fff',
    backgroundColor: '#1a2856',
    paddingLeft: 5,
    paddingRight: 5,
  },
  days: {
    borderColor: '#1a2856',
    borderWidth: 5,
    paddingTop: 10,
    paddingLeft: 16,
    paddingRight: 6,
    fontSize: 18,
  },
});
MIike Eps
  • 421
  • 7
  • 24

1 Answers1

1

Issues

Both loanAmountArray and durationArray are defined in the function body, so they are actually redeclared each render cycle, so any mutations you thought you did in the previous render cycle are wiped out. When this happens the style attribute is never able to match anything different each render.

Solution

  1. Since it seems you don't really update the elements of the array you can move the arrays out of the component. They can remain const and won't be redeclared each render.
  2. Don't bother trying to update the selected property of each element in the array, you can easily derive the selected state from the selectedDurationId and selectedAmountId state values and the current item.id when rendering.
  3. Use the extraData prop to indicate the list should rerender.

Code:

const loanAmountArray = [
  {
    id: 1,
    amount: "5,000",
    selected: "yes"
  },
  {
    id: 2,
    amount: "10,000",
    selected: "no"
  },
  {
    id: 3,
    amount: "20,000",
    selected: "no"
  }
];

const durationArray = [
  {
    id: 1,
    days: "30 days",
    rate: "3.3% Interest",
    selected: "yes"
  },
  {
    id: 2,
    days: "60 days",
    rate: "5% Interest",
    selected: "no"
  },
  {
    id: 3,
    days: "90 days",
    rate: "7% Interest",
    selected: "no"
  }
];

export default function Loans({}) {
  const [selectedDurationId, setSelectedDurationId] = useState(1);
  const [selectedAmountId, setSelectedAmountId] = useState(1);

  const changeSelectedDuration = function (id) {
    setSelectedDurationId(id);
  };

  const changeSelectedAmount = function (id) {
    setSelectedAmountId(id);
  };

  return (
    <View style={{ flex: 1 }}>
      <ScrollView>
        <View style={styles.contain}>
          <Text>Chose Loan Amount</Text>
          <FlatList
            numColumns={6}
            data={loanAmountArray}
            extraData={selectedAmountId} // <-- prop used to rerender
            keyExtractor={(item, index) => {
              return index.toString();
            }}
            renderItem={({ item }) => {
              return (
                <View>
                  <TouchableOpacity
                    onPress={() => {
                      changeSelectedAmount(item.id);
                    }}
                  >
                    <Text
                      style={
                        item.id === selectedAmountId // <-- match id property
                          ? styles.textBoxSelected
                          : styles.textBox
                      }
                    >
                      {item.amount}
                    </Text>
                  </TouchableOpacity>
                </View>
              );
            }}
          />

          <Text>Chose Payment Duration</Text>
          <FlatList
            numColumns={3}
            data={durationArray}
            extraData={selectedDurationId} // <-- prop used to rerender
            keyExtractor={(item, index) => {
              return index.toString();
            }}
            renderItem={({ item }) => {
              return (
                <View>
                  <TouchableOpacity
                    style={
                      item.id === selectedDurationId // <-- match id property
                        ? styles.durationViewPressed
                        : styles.durationView
                    }
                    onPress={() => {
                      changeSelectedDuration(item.id);
                    }}
                  >
                    <View>
                      <Text style={styles.interest}>{item.rate}</Text>
                    </View>
                    <View>
                      <Text style={styles.days}>{item.days}</Text>
                    </View>
                  </TouchableOpacity>
                </View>
              );
            }}
          />
        </View>
      </ScrollView>
    </View>
  );
}

Expo Snack Demo

enter image description here

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Thanks drew, but still not working after taking out the object array constants from the loan function, the views still don't change at run time after the user presses the TouchableOpacity of either amount or duration. Please advise me on how to make the style of the views update indicating the newly pressed option at runtime – MIike Eps May 25 '21 at 10:21
  • @MIikeEps I see. I updated my answer and added a running Expo Snack demo. Please check it out. – Drew Reese May 25 '21 at 15:32
  • Thanks a billion. You have quite literally saved my life – MIike Eps May 26 '21 at 10:25
  • Hi @Drew, Please help with question in below thrwead as well. Thanks https://stackoverflow.com/questions/69556738/unable-to-navigate-to-different-navigation-menu-while-using-mobx-for-state-stora – MIike Eps Oct 14 '21 at 07:40