React Native嵌套可折叠菜单:动态计算父容器高度求助
动态高度可折叠菜单解决方案
核心思路
放弃固定高度硬编码,通过获取内容真实高度+子组件高度变化回调实现动态适配,嵌套切换时父容器自动更新高度。
可折叠组件改造(支持动态高度)
import { useState, useRef, useEffect } from 'react'; import { Animated, TouchableOpacity, View, Text } from 'react-native'; const Collapsible = ({ title, children, onHeightChange }) => { const [expanded, setExpanded] = useState(false); const heightAnim = useRef(new Animated.Value(0)).current; const contentRef = useRef(null); useEffect(() => { if (expanded) { // 等待内容渲染完成后获取真实高度 requestAnimationFrame(() => { if (!contentRef.current) return; // React Native 环境用 measure 方法 contentRef.current.measure((_, __, ___, height) => { Animated.timing(heightAnim, { toValue: height, duration: 300, useNativeDriver: false, // 高度动画不支持原生驱动 }).start(() => onHeightChange?.(height)); }); // Web 环境替换为: // const height = contentRef.current.getBoundingClientRect().height; // Animated.timing(heightAnim, { ... }).start(() => onHeightChange?.(height)); }); } else { Animated.timing(heightAnim, { toValue: 0, duration: 300, useNativeDriver: false, }).start(() => onHeightChange?.(0)); } }, [expanded]); return ( <View style={{ borderBottomWidth: 1, borderBottomColor: '#eee' }}> <TouchableOpacity onPress={() => setExpanded(!expanded)} style={{ padding: 16 }} > <Text style={{ fontSize: 16 }}> {title} {expanded ? '▼' : '▶'} </Text> </TouchableOpacity> <Animated.View style={{ height: heightAnim, overflow: 'hidden' }}> <View ref={contentRef} style={{ paddingHorizontal: 16, paddingBottom: 16 }}> {children} </View> </Animated.View> </View> ); };
父容器适配嵌套高度变化
import { useState, useRef, useEffect } from 'react'; import { Animated, View } from 'react-native'; const MenuContainer = ({ data }) => { const [childHeights, setChildHeights] = useState({}); const parentHeightAnim = useRef(new Animated.Value(0)).current; // 计算父容器总高度并执行动画 useEffect(() => { const totalHeight = Object.values(childHeights).reduce((sum, h) => sum + h, 0); Animated.timing(parentHeightAnim, { toValue: totalHeight, duration: 300, useNativeDriver: false, }).start(); }, [childHeights]); // 接收子组件高度变化通知 const handleHeightChange = (itemId, height) => { setChildHeights(prev => ({ ...prev, [itemId]: height })); }; return ( <Animated.View style={{ height: parentHeightAnim, overflow: 'hidden' }}> {data.map(item => ( <Collapsible key={item.id} title={item.title} onHeightChange={(h) => handleHeightChange(item.id, h)} > {/* 嵌套子分类 */} {item.children && <MenuContainer data={item.children} />} </Collapsible> ))} </Animated.View> ); };
关键细节说明
- 真实高度获取:通过
ref获取渲染完成后的内容高度,避免固定值导致的适配问题 - 高度联动:子组件通过
onHeightChange回调向父组件传递高度变化,父组件汇总所有子项高度后更新自身动画 - 动画兼容性:高度动画必须关闭
useNativeDriver,因为原生驱动不支持布局属性动画
内容的提问来源于stack exchange,提问作者BHL
相关产品推荐
相关产品推荐

