You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native自定义Dropdown组件状态异常:点击后展开项不符合预期

问题:React Native自定义Dropdown组件无法实现单一展开项

开发React Native应用时,我写了一个自定义Dropdown组件,要求同一时间仅能展开一个列表项。组件用FlatList展示列表,点击Pressable元素展开对应项的详情。但现在点击某一项时,除选中项外的所有项都展开了。

我怀疑问题出在isExpanded状态的管理方式上,请问如何修改代码实现仅选中项展开、其余项折叠的效果?

附上当前组件代码:

import { 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>
    )
}

注:点击Channel时箭头向下,但其他组件的内容也被显示出来。


解决方案

问题根源

  1. isExpanded存储的是父组件传入的布尔状态值(比如channels的true/false),当多个项的状态值相同时,会同时匹配显示,导致所有对应状态的项都展开。
  2. 同时维护父组件的多个状态和内部isExpanded,状态逻辑混乱,无法精准控制单一展开项。

修改方案

核心思路:用内部状态isExpanded存储当前展开项的唯一key(比如'Channels'),而非布尔值,通过判断当前项的key是否等于isExpanded来控制展开/折叠,同时按需同步父组件状态(若不需要可直接移除)。

修改后的完整代码:

import { 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) {
    // 若父组件不需要单独控制每个项的状态,可移除下面这行及对应的setter
    const { setChannels, setFriends, setGroups, setServices } = props

    // 存储当前展开项的key,初始为null(全部折叠)
    const [isExpanded, setIsExpanded] = useState<string | null>(null)

    const toggleState = (key: string) => {
        // 点击的项已展开则关闭,否则展开该项并折叠其他项
        const newExpandedKey = isExpanded === key ? null : key
        
        setIsExpanded(newExpandedKey)

        // 若父组件需要同步状态,可保留下面的逻辑,否则删除
        switch (key.toLowerCase()) {
            case 'channels':
                setChannels(newExpandedKey === 'Channels')
                break;
            case 'friends':
                setFriends(newExpandedKey === 'Friends')
                break
            case 'groups':
                setGroups(newExpandedKey === 'Groups')
                break
            case 'services':
                setServices(newExpandedKey === 'Services')
                break
            default:
                break;
        }
    }

    return (
        <View style={styles.dropDown}>
            <FlatList
                data={[
                    { key: 'Channels', value: 'channel' },
                    { key: 'Friends', value: 'friend' },
                    { key: 'Groups', value: 'group' },
                    { key: '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={isExpanded === item.key ? "caret-down" : "caret-left"} 
                                style={{ color: '#626262' }} 
                            />
                        </Pressable>
                        {/* 判断当前项的key是否等于isExpanded,是则显示详情 */}
                        {isExpanded === item.key && (
                            <View>
                                <Text>{item.value}</Text>
                            </View>
                        )}
                    </View>
                )}
            />
        </View>
    )
}

关键修改点

  • 将isExpanded改为存储展开项的key(如'Channels'),而非布尔值,确保唯一匹配。
  • toggleState函数逻辑改为:点击已展开的项则关闭,点击未展开的项则展开该项并自动折叠其他项。
  • 箭头图标和详情显示的判断逻辑统一为isExpanded === item.key,精准控制单个项的状态。
  • 移除了data中多余的state字段,不再依赖父组件的布尔状态,简化逻辑。

如果父组件不需要单独维护每个项的展开状态,可以直接删除所有与setChannels等相关的代码,进一步简化组件。


内容的提问来源于stack exchange,提问作者noor soreti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 22:29:58