如何在 React-Native (JavaScript) 中显示文本更多/更少

问题描述 投票:0回答:9

我正在开发反应本机应用程序。其中,我们在Text上显示一些描述,可能是行数。

所以,如果数据超过 3 行,如果展开,我必须显示更多和更少。

        <FlatList
          style={styles.faltList}
          showsVerticalScrollIndicator
          data={data}
          extraData={this.state}
          renderItem={({ item, index }) => (
            <View style={styles.flatListCell}>
                <Text style={styles.description}>{item.description}</Text>
              </View>
            </View>
          )
          }
          ItemSeparatorComponent={() => (
            <View style={{ height: 10}} />
          )}
        />

我找到了 react-native-view-more-text 库,但我想通过自定义代码来实现它。

注意:我正在 FlatList 中显示该文本。

有什么建议吗?

javascript react-native text react-native-flatlist
9个回答
45
投票

我尝试过这种方式,希望对你和其他人有帮助!

const postTextContent = (props) => {
const [textShown, setTextShown] = useState(false); //To show ur remaining Text
const [lengthMore,setLengthMore] = useState(false); //to show the "Read more & Less Line"
const toggleNumberOfLines = () => { //To toggle the show text or hide it
    setTextShown(!textShown);
}

const onTextLayout = useCallback(e =>{
    setLengthMore(e.nativeEvent.lines.length >=4); //to check the text is more than 4 lines or not
    // console.log(e.nativeEvent);
},[]);
    
  return (
      <View style={styles.mainContainer}>
          <Text
              onTextLayout={onTextLayout}
              numberOfLines={textShown ? undefined : 4}
              style={{ lineHeight: 21 }}>{Your Long Text}</Text>

              {
                  lengthMore ? <Text
                  onPress={toggleNumberOfLines}
                  style={{ lineHeight: 21, marginTop: 10 }}>{textShown ? 'Read less...' : 'Read more...'}</Text>
                  :null
              }
      </View>
  )
}

4
投票

第一个实现已经很接近,但问题是当文本等于 3 行时,“阅读更多”按钮会显示,但实际上不应该显示,因为没有更多文本了。我通过更新状态行数以及检查文本是否已显示来修复它。

const ReadMoreText = ({ readMoreStyle, text, textStyle }) => {
  const [showMoreButton, setShowMoreButton] = useState(false);
  const [textShown, setTextShown] = useState(false);
  const [numLines, setNumLines] = useState(undefined);

  const toggleTextShown = () => {
    setTextShown(!textShown);
  };

  useEffect(() => {
    setNumLines(textShown ? undefined : 3);
  }, [textShown]);

  const onTextLayout = useCallback(
    (e) => {
      if (e.nativeEvent.lines.length > 3 && !textShown) {
        setShowMoreButton(true);
        setNumLines(3);
      }
    },
    [textShown],
  );

  return (
    <>
      <Text onTextLayout={onTextLayout} numberOfLines={numLines} style={textStyle} ellipsizeMode="tail">
        {text}
      </Text>

      {showMoreButton ? (
        <Text onPress={toggleTextShown} style={readMoreStyle}>
          {textShown ? 'Read Less' : 'Read More'}
        </Text>
      ) : null}
    </>
  );
};

3
投票

您可以简单地使用

numberOfLines
,这是一个
<Text>
道具:

用于在计算文本后用省略号截断文本 布局,包括换行,使得总行数 不超过这个数字。

而且,显然,有一个足够的逻辑处理程序可以在您的

state
中保存显示哪些文本以及截断哪些文本。

让我们看一个我刚刚创建的示例:

state = {
    textShown: -1,
  };

  toggleNumberOfLines = index => {
    this.setState({
      textShown: this.state.textShown === index ? -1 : index,
    });
  };

  render() {
    return (
      <View style={styles.container}>
        <FlatList
          data={[
            { key: 'a', description: longText },
            { key: 'b', description: longText },
            { key: 'c', description: longText },
          ]}
          renderItem={({ item, index }) => (
            <View style={styles.flatListCell}>
              <Text
                numberOfLines={this.state.textShown === index ? undefined : 3}
                style={styles.description}>
                {longText}
              </Text>
              <Text
                onPress={() => this.toggleNumberOfLines(index)}
                style={{ color: 'red' }}>
                {this.state.textShown === index ? 'read less...' : 'read more...'}
              </Text>
            </View>
          )}
        />
      </View>
    );
  }

这里我使用

state
来保存显示的
FlatList
中元素的索引。如果没有显示,则保存的值为-1。

您可以在this零食中尝试它的行为,(我希望)重现您的情况。 如果这就是您要找的,请告诉我。 (嗨,Anilkumar,我们已经见过了:))


1
投票

看看我的解决方案,我没有使用文本行,而是使用文本长度。

import React, {useState} from 'react';
import {Text, View, Image, TouchableOpacity, StyleSheet} from 'react-native';

const PostContent = () => {
  const postDescription =
    "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
  const [showMore, setShowMore] = useState(false);

  return (
    <View style={styles.postContentContainer}>
      {postDescription.length > 120 ? (
        showMore ? (
          <TouchableOpacity onPress={() => setShowMore(!showMore)}>
            <Text style={styles.postDescription}>{postDescription}</Text>
            <Text style={styles.seeMore}>Show less</Text>
          </TouchableOpacity>
        ) : (
          <TouchableOpacity onPress={() => setShowMore(!showMore)}>
            <Text style={styles.postDescription}>
              {`${postDescription.slice(0, 120)}... `}
            </Text>
            <Text style={styles.seeMore}>Show more</Text>
          </TouchableOpacity>
        )
      ) : (
        <Text style={styles.postDescription}>{postDescription}</Text>
      )}
    </View>
  );
};

export default PostContent;

const styles = StyleSheet.create({
  postContentContainer: {
    // borderWidth: 1,
    // borderColor: 'red',
    flexDirection: 'column',
  },

  postMedia: {
    //borderWidth: 1,
    //borderColor: 'red',
    width: '100%',
    height: 280,
    resizeMode: 'cover',
  },

  postDescription: {
    paddingTop: 15,
    paddingHorizontal: 15,
    color: colors.text.black,
    fontFamily: fonts.family.body,
    fontSize: fonts.fontSizes.button,
    fontWeight: fonts.fontWeights.thin,
  },

  seeMore: {
    paddingHorizontal: 15,
    color: colors.text.grey,
    fontStyle: 'italic',
    textDecorationLine: 'underline',
    fontFamily: fonts.family.body,
    fontSize: fonts.fontSizes.button,
    fontWeight: fonts.fontWeights.thin,
  },
});

1
投票

虽然有点晚了,但我已经为此开发了一个非常优化的、轻量级的解决方案。我称之为 react-native-more-or-less

以下是如何使用它的示例:

import { MoreOrLess } from "react-native-more-or-less";

// ...

export default function App() {
  return (
    <MoreOrLess numberOfLines={3} textComponent={CustomText}>
      Lorem Ipsum is simply dummy text of the printing and typesetting industry.
      Lorem Ipsum has been the industry's standard dummy text ever since the
      1500s, when an unknown printer took a galley of type and scrambled it to
      make a type specimen book. It has survived not only five centuries, but
      also the leap into electronic typesetting, remaining essentially
      unchanged. It was popularised in the 1960s with the release of Letraset
      sheets containing Lorem Ipsum passages, and more recently with desktop
      publishing software like Aldus PageMaker including versions of Lorem
      Ipsum.
    </MoreOrLess>
  );
}

统计:https://bundlephobia.com/package/[电子邮件受保护]


0
投票

当我试图找到一种限制行数但允许文本扩展的方法时,我查看了react-native

Text
组件,尽管它可以将文本截断为您指定的行数,它不显示文本是否实际上被截断。

这意味着您可以添加一个按钮来显示更多文本,但即使文本没有被截断,它也会显示出来。

如果基于字符数量的截断是可以接受的,您可以创建一个接受 React 元素的组件(它不需要只是字符串),解析输入,访问元素的子元素,直到到达字符串,然后计算字符串中的字符数量,在达到最大字符数量后截断并停止进程。

以下是执行此操作的组件示例:

import React from 'react';

type ParseResult = {
    content: React.ReactNode | React.ReactNode[];
    remaining: number;
};

type Parse = (root: ParseResult, parse: Parse) => ParseResult;

const parseString = (text: string, remaining: number): ParseResult => {
    if (!text?.length) {
        return { content: text, remaining };
    }

    const newText =
        remaining <= 0
            ? ''
            : text.length > remaining
            ? text.substring(0, remaining)
            : text;

    return { content: newText, remaining: remaining - text.length };
};

const parse: Parse = (root, parseCallback) => {
    const { content, remaining = 0 } = root;

    if (typeof content === 'string') {
        const result = parseString(content, remaining);
        return result;
    } else if (React.isValidElement(content)) {
        const children = (content.props as { children?: React.ReactNode })
            ?.children;

        if (children != null) {
            const innerResult = parseCallback(
                {
                    content: children,
                    remaining,
                },
                parseCallback,
            );
            const newContent = React.cloneElement(content, {}, innerResult.content);
            return { content: newContent, remaining: innerResult.remaining };
        }
    } else if (Array.isArray(content)) {
        const list: React.ReactNode[] = [];
        let newRemaining = remaining;

        for (const child of content) {
            const parsed = parseCallback(
                { content: child, remaining: newRemaining },
                parseCallback,
            );
            newRemaining = parsed.remaining;
            const newContent = (
                <React.Fragment key={list.length}>{parsed.content}</React.Fragment>
            );
            list.push(newContent);

            if (newRemaining < 0) {
                break;
            }
        }

        return { content: list, remaining: newRemaining };
    }

    return root;
};

export interface ReadMoreAdditionalProps {
    expanded?: boolean;
    showMore?: React.ReactNode;
    showLess?: React.ReactNode;
}

const ReadMoreInner: React.FC<
    {
        children: React.ReactNode;
        truncate: number;
    } & ReadMoreAdditionalProps
> = ({ truncate, expanded, showMore, showLess, children }) => {
    const root: ParseResult = {
        content: children,
        remaining: truncate,
    };
    const result = parse(root, parse);

    return (
        <>
            {expanded ? children : result.content}
            {result.remaining < 0 ? (expanded ? showLess : showMore) : undefined}
        </>
    );
};

export interface ReadMoreOptions extends ReadMoreAdditionalProps {
    truncate?: number;
}

export type ReadMoreProps = ReadMoreOptions & {
    children: React.ReactNode;
};

export const ReadMore: React.FC<ReadMoreProps> = ({
    truncate,
    children,
    ...props
}) => {
    return truncate != null ? (
        <ReadMoreInner {...props} truncate={truncate}>
            {children}
        </ReadMoreInner>
    ) : (
        <>{children}</>
    );
};

请记住,该组件希望子组件包含最终文本(或不包含文本)。它不会访问其他属性(例如

text
title
属性),它们将被忽略。

您可以在代码中包含此组件,添加按钮以显示更多或更少的文本,然后按照您想要的方式设置按钮的样式。

如果您不想在项目中维护组件代码,我创建了一个库来执行此操作。

安装库:

npm install react-shorten

以下是如何使用该库的示例:

import React from 'react';
import { ReadMore } from 'react-shorten';
import type { StyleProp, TextStyle } from 'react-native';
import { Text } from 'react-native';

export interface ReadMoreNativeProps {
    truncate: number | undefined;
    showMoreText?: React.ReactNode;
    showLessText?: React.ReactNode;
    style?: StyleProp<TextStyle>;
    btnStyle?: StyleProp<TextStyle>;
    children: React.ReactNode;
}

export const ReadMoreNative: React.FC<ReadMoreNativeProps> = ({
    truncate,
    showMoreText,
    showLessText,
    style,
    btnStyle,
    children,
}) => {
    const [expanded, setExpanded] = React.useState(false);

    const onShowMore = React.useCallback(() => setExpanded(true), []);

    const onShowLess = React.useCallback(() => setExpanded(false), []);

    return (
        <ReadMore
            truncate={truncate}
            expanded={expanded}
            showMore={
                <Text style={style}>
                    {'... '}
                    <Text onPress={onShowMore} style={btnStyle}>
                        {showMoreText ?? 'Show more'}
                    </Text>
                </Text>
            }
            showLess={
                <Text style={style}>
                    {' '}
                    <Text onPress={onShowLess} style={btnStyle}>
                        {showLessText ?? 'Show less'}
                    </Text>
                </Text>
            }
        >
            Lorem ipsum dolor sit amet,{' '}
            <Text style={{ fontStyle: 'italic' }}>
                <Text style={{ textDecorationLine: 'underline' }}>
                    consectetur adipiscing elit. Sed ullamcorper, odio eu aliquam
                    ultricies, enim sapien aliquet arcu, quis aliquam diam massa eu
                    nisl. Sed vitae nunc eget nunc ullamcorper aliquet.
                </Text>
            </Text>{' '}
            Sed euismod, nisl eget aliquam ultricies, justo nisl aliquet nunc,
            quis aliquam diam massa eu nisl. Sed vitae nunc eget nunc
            ullamcorper aliquet.
        </ReadMore>
    );
};

您可以在此处查看网络演示,在此处查看本机演示。

如果您不想安装其他库(如OP中所要求的),您还可以使用演示作为参考,并将上面的

ReadMore
组件直接包含在您的项目中。

具体就 OP 代码而言,指定为

<Text style={styles.description}>{item.description}</Text>
的代码应包含在
ReadMore
组件内。


0
投票
const MoreLessComponent = ({ truncatedText, fullText }: any) => {
    const [more, setMore] = useState(false);
    return (
        <Text style={styles.text}>
            {!more ? `${truncatedText}...` : fullText}
            <TouchableOpacity onPress={() => setMore(!more)}>
                <Text>{more ? ' See less' : ' See more'}</Text>
            </TouchableOpacity>
        </Text>
    );
    };



const MoreInfo = ({ text, linesToTruncate }: any) => {
const [clippedText, setClippedText] = useState('');
return clippedText ? (
    <MoreLessComponent truncatedText={clippedText} fullText={text} />
) : (
    <Text
        style={styles.text}
        numberOfLines={clippedText ? linesToTruncate : 3}
        ellipsizeMode={'tail'}
        onTextLayout={event => {
            const { lines } = event.nativeEvent;
            if (lines.length > linesToTruncate) {
                const textInfo = lines
                    .splice(0, linesToTruncate)
                    .map(line => line.text)
                    .join('');
                setClippedText(textInfo.slice(0, textInfo.length - 20));
            }
        }}
    >
        {text}
    </Text>
);};

<MoreInfo text={info} linesToTruncate={2} />

enter image description here enter image description here enter image description here


-1
投票
import React from 'react';
import PropTypes from 'prop-types';
import AnchorText from '../AnchorText';

import { StyledContainer, RegularText } from './styles';

export default class Description extends React.Component {
      constructor(props) {
super(props);
this.state = {
  showMore: true,
};
}

 onClick = () => {
this.setState({
  showMore: false,
});
};

 render() {
const { description } = this.props;
const { showMore } = this.state;
if (!description) return null;

return (
  <StyledContainer FD={'column'}>
    {showMore ? (
      <RegularText MT={1}>
        {description.slice(0, 150)}{' '}
        {description.length > 150 && (
          <AnchorText onClick={this.onClick} label=" .. Read more" />
        )}{' '}
      </RegularText>
    ) : (
        <RegularText MT={1}>{description}</RegularText>
    )}
  </StyledContainer>
);
 }
} 

Description.propTypes = {
 description: PropTypes.string,
 };

看看这个小部件


-2
投票

check output of this code

state = {
    textLenth: null,
    numberOfLines: 3,
}
handleSeeMore = () => {
    this.state.textLenth
    ? this.setState({numberOfLines: 0})
    : this.setState({numberOfLines: 3});
};

     <Text
     numberOfLines={this.state.numberOfLines}
     onPress={() => this.handleSeeMore()}
     ellipsizeMode="middle"
     onTextLayout={({nativeEvent: {lines}}) =>
     this.setState({textLenth: lines.length === 3})
     }>
         This Gig Take a glance at the showcase of our artistic work:
         Modern and Trendy Logo Artworkslkjfkljf ksnfksfnsf Mascot &
         Custom Logo efdfg Artworks:lk knnk 'Explore the
         ultimate Experience..!' To fulfill your designing needs, 
         Make us Graphically Yours...!! Why Team StrideInIt? We 
         believe in our
     {'                      '}
         {this.state.textLenth && (
         <Text
         color="red"
         onPress={() => this.setState({numberOfLines: 0})}>
             see more
         </Text>
         )}
     </Text>
© www.soinside.com 2019 - 2024. All rights reserved.