Search code examples
reactjsreact-nativereact-native-flatlistreact-flatlist

Managing state in custom dropdown component using flatlist


I'm building a React Native app with a custom dropdown component where one element can be expanded at a time. The component uses a FlatList to display a list of items, and each item can be expanded to show more details when pressed. I'm managing the expanded state using useState, but when I press on one of the Pressable elements, all items except the one I selected are expanded.

Here's the code for my CustomDropdown component:

import { createContext, useContext, useState } from "react";
import { Text, View, StyleSheet, FlatList, Pressable } from "react-native";
import FontAwesome from '@expo/vector-icons/FontAwesome';

export default function CustomDropdown(props: any) {
    const { channels, friends, groups, services, setChannels, setFriends, setGroups, setServices } = props

    const [ isExpanded , setIsExpanded] = useState(null)

    const toggleState = (key) => {            
        switch (key.toLowerCase()) {
          case 'channels':
            setChannels((prev: any)=>!prev)
            setIsExpanded(channels)
            break;
          case 'friends':
            setFriends((prev: any)=>!prev)
            setIsExpanded(friends)
            break
          case 'groups':
            setGroups((prev: any)=>!prev)
            setIsExpanded(groups)
            break
          case 'services':
            setServices((prev: any)=>!prev)
            setIsExpanded(services)
            break
          default:
            break;
        }
      }

    return( 
        <View style={styles.dropDown}>
              <FlatList 
                  data={[
                  {key: 'Channels', state: channels, value: 'channel'},
                  {key: 'Friends', state: friends, value: 'friend'},
                  {key: 'Groups', state: groups, value: 'group',},
                  {key: 'Services', state: services,  value: 'service'},
                  ]}
                  renderItem={({item}) => 
                  <View>
                    <Pressable onPress={() => toggleState(item.key)} style={styles.flatList}>
                      <Text style={styles.flatListItem}>{item.key}</Text>
                      <FontAwesome size={20} name={ item.state ? "caret-down" :"caret-left"} style={{color: '#626262'}} />
                  </Pressable>
                  {
                    isExpanded == item.state ?
                    <View>
                      <Text>{item.value}</Text>
                    </View>
                    : null
                  }
                  </View>
                }
                />
            </View>
    )
}

I suspect the issue might be with how I'm managing the isExpanded state. How can I modify my code so that only the selected item is expanded, and the others are collapsed?

Edit: Here is a screenshot of my issue. As you can see, when Channel is selected the arrow icon is pointing down however the contents of the other components are being shown instead!


Solution

  • I eventually figured out how to get one element to toggle at a time. In my FlatList I just set isExpanded == item.value in the rather than isExpanded == item.state. Also in my toggleState function, I used setExpanded() to check the current value of isExpanded before setting it either to false or the value of the switch/case statement.

    If there are any better ways to achieve this functionality I would still like to hear and improve on the logic.

    export default function CustomHomeDropdown(props: any) {
        const { channels, friends, groups, services, setChannels, setFriends, setGroups, setServices } = props
    
        const [ isExpanded , setIsExpanded] = useState(false)
    
        const toggleState = (key: string) => {
          switch (key.toLowerCase()) {
            case 'channels':
                setChannels((prev: any) => !prev);
                setIsExpanded(isExpanded === 'channels' ? false : 'channels');
                break;
            case 'friends':
                setFriends((prev: any) => !prev);
                setIsExpanded(isExpanded === 'friends' ? false : 'friends');
                break;
            case 'groups':
                setGroups((prev: any) => !prev);
                setIsExpanded(isExpanded === 'groups' ? false : 'groups');
                break;
            case 'services':
                setServices((prev: any) => !prev);
                setIsExpanded(isExpanded === 'services' ? false : 'services');
                break;
            default:
                break;
            }
          }
    
        return( 
            <View style={styles.dropDown}>
                  <FlatList 
                      data={[
                      {key: 'Channels', state: channels, value: 'channels'},
                      {key: 'Friends', state: friends, value: 'friends'},
                      {key: 'Groups', state: groups, value: 'groups',},
                      {key: 'Services', state: services,  value: 'services'},
                      ]}
                      renderItem={({item}) => 
                      <View>
                        <Pressable onPress={() => toggleState(item.key)} style={styles.flatList}>
                          <Text style={styles.flatListItem}>{item.key}</Text>
                          <FontAwesome size={20} name={ item.state ? "caret-down" :"caret-left"} style={{color: '#626262'}} />
                      </Pressable>
                      {
                        isExpanded == item.value ?
                        <View style={styles.dropDownItem}>
                          <Text>{item.value}</Text>
                        </View>
                        : null
                      }
                      </View>
                    }
                    />
                </View>
        )
    }