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

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>
  );
};

关键细节说明

  1. 真实高度获取:通过ref获取渲染完成后的内容高度,避免固定值导致的适配问题
  2. 高度联动:子组件通过onHeightChange回调向父组件传递高度变化,父组件汇总所有子项高度后更新自身动画
  3. 动画兼容性:高度动画必须关闭useNativeDriver,因为原生驱动不支持布局属性动画

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 09:56:13