Question
How can I get the number of lines of a text before it is rendered?
I have implemented my own "CollapsibleText" (the typical read more/less text), but I am experiencing a bug because of calculating the number of lines with the "onLayout" prop.
I just want to render a short version of the original text with a "read more button"... but, to work with the "onLayout" prop, the full text has to be rendered before hidden it.
My current code works... but I need to avoid rendering the full text before the total lines calculation... is it possible?
Take a look at this snack, to notice the bug (test on iOS or Android devices, not in web).
Code
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(false);
const [showReadMoreButton, setShowReadMoreButton] = useState(false);
const toggleIsCollapsed = () => {
setIsCollapsed((prevIsCollapsed) => !prevIsCollapsed);
};
const handleOnTextLayout = useCallback(
({ nativeEvent: { lines } }) => {
if (isMounted() && !measured.current && lines.length > numberOfLines) {
measured.current = true;
setIsCollapsed(true);
setShowReadMoreButton(true);
}
},
[isMounted]
);
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
numberOfLines={isCollapsed ? numberOfLines : undefined}
ellipsizeMode={ellipsizeMode}
onTextLayout={handleOnTextLayout}
style={textStyle}
>
{children}
</Text>
{showReadMoreButton && renderReadMoreButton()}
</View>
);
}
CodePudding user response:
How about... you just initialize the isCollapsed
state with true
?
const [isCollapsed, setIsCollapsed] = useState(true);
CodePudding user response:
You need to hide the All the text
function truncate(str, n){
return str?.length . n ? str.substr(0, n - 1) "..." : str;
}
<Text>{truncate(movie?.overview, 150