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时箭头向下,但其他组件的内容也被显示出来。
解决方案
问题根源
isExpanded存储的是父组件传入的布尔状态值(比如channels的true/false),当多个项的状态值相同时,会同时匹配显示,导致所有对应状态的项都展开。- 同时维护父组件的多个状态和内部
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
相关产品推荐
相关产品推荐

