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

React Native 文本渲染前获取行数 解决CollapsibleText折叠组件BUG

解决方案

你遇到的闪烁问题根因是初始isCollapsed状态为false,首次渲染会先加载完整文本,等onTextLayout回调触发后才切换到折叠状态,两者的时间差导致了可见的内容跳变。该需求可以实现,不需要预渲染可见的完整文本,用隐藏测量层的方案即可解决:

核心逻辑

新增一个和展示文本完全同样式的测量节点,设置为绝对定位、透明度为0、不占布局空间,全程对用户不可见,专门用来计算文本总行数。展示文本初始直接按折叠状态渲染,不会出现完整文本闪现的问题。

优化后代码

import { useEffect, useRef, useState, useCallback } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { useTheme } from '@react-navigation/native'; // 按需替换为你实际的主题引入
import { useLanguage, useIsMounted } from '你的工具路径'; // 按需替换为实际引入路径

export default function CollapsibleText({
  children = "",
  numberOfLines = 3,
  ellipsizeMode = "tail",
  containerStyle,
  textStyle = styles.text,
  readMoreButtonStyle = styles.readMoreButton,
  readMoreTextStyle = styles.readMoreText,
}) {
  const { colors } = useTheme();
  const isMounted = useIsMounted();
  const { t } = useLanguage();

  const measured = useRef(false);
  const [isCollapsed, setIsCollapsed] = useState(true); // 初始直接为折叠态
  const [showReadMoreButton, setShowReadMoreButton] = useState(false);

  const toggleIsCollapsed = () => {
    setIsCollapsed((prevIsCollapsed) => !prevIsCollapsed);
  };

  const handleOnMeasure = useCallback(
    ({ nativeEvent: { lines } }) => {
      if (isMounted() && !measured.current && lines.length > numberOfLines) {
        measured.current = true;
        setShowReadMoreButton(true);
      }
    },
    [isMounted, numberOfLines]
  );

  // 文本内容、样式、行数限制变更时重置测量状态
  useEffect(() => {
    measured.current = false;
    setShowReadMoreButton(false);
    setIsCollapsed(true);
  }, [children, textStyle, numberOfLines]);

  const renderReadMoreButton = () => (
    <TouchableOpacity
      activeOpacity={0.75}
      onPress={toggleIsCollapsed}
      style={readMoreButtonStyle}
    >
      <Text style={[{ color: colors.silverChalice }, readMoreTextStyle]}>
        {t(`collapsibleText.${isCollapsed ? "readMore" : "readLess"}`)}
      </Text>
    </TouchableOpacity>
  );

  return (
    <View style={containerStyle}>
      {/* 隐藏测量节点,全程不可见 */}
      <Text
        style={[textStyle, { position: 'absolute', opacity: 0, zIndex: -1 }]}
        onTextLayout={handleOnMeasure}
      >
        {children}
      </Text>
      {/* 实际展示的文本节点,初始就按折叠态渲染 */}
      <Text
        numberOfLines={isCollapsed ? numberOfLines : undefined}
        ellipsizeMode={ellipsizeMode}
        style={textStyle}
      >
        {children}
      </Text>

      {showReadMoreButton && renderReadMoreButton()}
    </View>
  );
}

// 示例样式,按需替换
const styles = {
  text: { fontSize: 14, lineHeight: 20 },
  readMoreButton: { marginTop: 4 },
  readMoreText: { fontSize: 13 }
};

说明

  • 测量节点不会占用布局空间,也不会被用户看到,不存在闪烁问题
  • 新增了内容、样式、行数限制变更时的重置逻辑,避免动态更新时计算错误
  • 修正了原代码中useCallback依赖项缺失的问题,避免潜在逻辑异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 02:15:04