Pasindu Weerakoon
Pasindu Weerakoon

Reputation: 628

How to handle a single button at a time in React Native FlatList?

I'm working on a react native project and I'm really new to this technology.

I have a dynamic list (data fetching from the server). Each item on that list has two buttons, "YES" and "NO" as follows

enter image description here

Scenario: If the user clicks the "YES" button that needs to be highlighted in green color, otherwise user clicked on the "NO" button style pick should be added to the "NO" button. Likewise, the user can select multiple buttons in that list as follows.

enter image description here

My problem is, Now If I select one button from my list, that color will appear to the complete list as follows. If 1st item "YES" button selected all the YES buttons will be displayed as follows.

enter image description here

I did lots of code changes to fix this issue, but they did not work for me.

I have added my code below, Please let me know where it is going wrong. Thanks in advance.

checkList.tsx

    const [isYesButtonEnabled, setIsYesButtonEnabled] = useState(false);
    const [isNoButtonEnabled, setIsNoButtonEnabled] = useState(false);

    const [checkLisItems, setCheckListItems] = useState({
        isLoading: true,
        checklistData: null,
    });

    const yesClicked = () => {
        setIsYesButtonEnabled(true);
        setIsNoButtonEnabled(false);
    }

    const noClicked = () => {
        setIsYesButtonEnabled(false);
        setIsNoButtonEnabled(true);
    }

            <View style={{ flex: 4 }}>
                <FlatList
                    showsHorizontalScrollIndicator={false}
                    data={checkLisItems.checklistData}
                    renderItem={({ item }) => {
                        return (
                            <InspectionCheckListItem
                                itemData={item}
                                yesClicked={() => yesClicked()}
                                noClicked={() => noClicked()}
                                isYesButtonEnabled={isYesButtonEnabled}
                                isNoButtonEnabled={isNoButtonEnabled}
                            />
                        );
                    }}
                    keyExtractor={item => `${item.chelistid}`}
                />
            </View>

            

InsprctionCheckListRow.tsx

import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet, TextInput } from 'react-native';
import { COLORS, FONT_FAMILY } from '../../../../constants/Component.styles';

type props = {
    isYesButtonEnabled?: boolean;
    isNoButtonEnabled?: boolean;
    yesClicked?: () => void;
    noClicked?: () => void;
}

const InsprctionCheckListItem = props => {
    return (
        <View style={styles.container}>
            <View style={{ flex: 1, alignSelf: 'center' }} >
                <Text style={styles.allText}>{props.itemData.checklistName}</Text>
            </View>
            <View style={{ flex: 1, flexDirection: 'row', alignSelf: 'center', justifyContent: 'center' }} >
                <TouchableOpacity style={props.isYesButtonEnabled ? styles.touchButtonClicked : styles.touchButton} onPress={props.yesClicked}>
                    <Text style={styles.touchButtonWithoutClick}>YES</Text>
                </TouchableOpacity>
                <TouchableOpacity style={props.isNoButtonEnabled ? styles.touchButtonClicked : styles.touchButton} onPress={props.noClicked}>
                    <Text style={styles.touchButtonWithoutClick}>NO</Text>
                </TouchableOpacity>
            </View>
            <View style={{ flex: 1 }} >
                <TextInput style={styles.textInput} onChangeText={props.changedText} defaultValue={props.itemData.remark}/>
            </View>
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        alignItems: 'center',
        justifyContent: 'flex-start',
        flexDirection: 'row',
        marginTop: '3%',
    },
    allText: {
        fontSize: 20,
        color: COLORS.BLUE_2C,
        fontFamily: FONT_FAMILY.LIGHT,
        justifyContent: 'center',
        alignSelf: 'center'
    },
    textInput: {
        fontSize: 20,
        color: COLORS.BLUE_2C,
        fontFamily: FONT_FAMILY.LIGHT,
        borderWidth: 0.5,
        borderRadius: 5,
        borderColor: COLORS.BLUE_69,
        backgroundColor: 'rgba(0,0,0,0)',
    },
    touchButton: {
        width: 80,
        height: 40,
        borderRadius: 5,
        borderWidth: 0.8,
        borderColor: COLORS.ASH_AE,
        justifyContent: 'center',
        alignSelf: 'center',
        textAlign: 'center',
        margin: '0.2%',
        backgroundColor: COLORS.ASH_AE,
    },
    touchButtonClicked: {
        width: 80,
        height: 40,
        borderRadius: 5,
        borderWidth: 0.8,
        borderColor: COLORS.PINK,
        justifyContent: 'center',
        alignSelf: 'center',
        textAlign: 'center',
        backgroundColor: COLORS.PINK,
    },
    touchButtonWithoutClick: {
        fontSize: 14,
        color: COLORS.WHITE,
        fontFamily: FONT_FAMILY.LIGHT,
        justifyContent: 'center',
        alignSelf: 'center',
        textAlign: 'center'
    }
});

export default InsprctionCheckListItem;

Sample Server Response

"ChecklistsItem": [
            {
                "chelistid": 123,
                "ireqstId": 12,
                "checklistName": "Name and address of the pharmacy",
                "validity": "Valid",
                "remark": null
            }
        ],

Upvotes: 1

Views: 842

Answers (2)

Hassan Kandil
Hassan Kandil

Reputation: 1866

If you pass one boolean state to all the choices , changing one will change the state which will affect all the choices connected to it. But you can push the item that has been choose (Yes) into an array and for each choice group check if that item exists in the choice array by array.findIndex() method, if it is then its yes if not then its no , check the following example:

//add new array that will carry the elements that marked as yes
  const [arrayOfYes, setArrayOfYes] = useState([]);

//Modify yes clicked by pushing the clicked yes into the new array
  const yesClicked = (element) => {
  //Check first that it doesn't exist inside the array to avoid 
  //duplicates
  const doesntExist = arrayOfYes.findIndex(x => x.chelistid === 
   element.chelistid) === -1
    if (doesntExist){
        setArrayOfYes([...arrayOfYes, element]);
    }
}

//Modify no clicked by remove the clicked no if it exists in the array
  const noClicked = (element) => {
    const index = arrayOfYes.findIndex(x => x.chelistid === element. 
   chelistid)
  if (index !== -1){
     //Remove the element from the array
     var modifiedArray = [...arrayOfYes]
     modifiedArray.splice(index, 1);
     setArrayOfYes(modifiedArray)
  }
}

//Pass the element parameter and update yes and no enabled
  <InspectionCheckListItem
   itemData={item}
   yesClicked={() => yesClicked(item)}
   noClicked={() => noClicked(item)}
   //now check if the current item exists in the array by finding the index
   isYesButtonEnabled={arrayOfYes.findIndex(x => x.chelistid === item. 
   chelistid)!== -1} //If the index = -1 that means the item doesn't exist 
   isNoButtonEnabled={arrayOfYes.findIndex(x => x.chelistid === item. 
   chelistid)== -1} //If The element is not yes then it is no
  />

Upvotes: 1

Quang Th&#225;i
Quang Th&#225;i

Reputation: 697

The problem in your code is: You are using one Y/N state to handle all the items in list!

So that, if one item in list changes the Y/N state, the others will change, because they share the same Y/N state for rendering.

My suggestion:

When you receive data from backend, you could append one more property to each item for handling Yes/No state, eg:

ChecklistsItem = dataResponse.map(item => {
    return {
       ...item,
       enabled: true // here!
   }
}

Then, when you render item in list, check that property to ensure that the item is checked Yes or not.

Upvotes: 1

Related Questions