React/NextJS开发ReadMore组件报text.substr is not a function错误
问题根因
这个报错和Next.js的特殊机制无关,本质是类型错误:
你在组件中直接对children调用字符串原型方法substr(),但你实际传入的children是包裹了文本的<p>标签React元素对象,并非字符串类型,对象上不存在substr方法,因此抛出TypeError。
React中children的类型不固定,根据传入内容的不同,它可能是字符串、数字、React元素、React节点数组,甚至是null/undefined,直接假定它为字符串调用字符串方法必然存在风险。
修复方案
根据你的业务场景二选一即可:
- 如果不需要保留传入文本的外层标签,直接给组件传纯字符串作为children即可,原有组件逻辑无需修改
- 如果需要支持传入带JSX/HTML标签的内容,需要先提取children中的纯文本做长度判断和截断,再按需渲染
方案1:传纯文本调用(改动最小)
去掉ReadMore子内容外层的<p>标签,直接传入纯文本:
<ReadMore limit={200}> Lorem ipsum dolor sit amet, consectetur lorem, sed do eiusmod tempor incididunt ut lab lorem. Ut enim ad minim veniam,Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nobis consectetur molestias provident perferendis voluptates veniam accusantium, quibusdam quod, dignissimos iusto, eum error itaque ab harum. Illum sequi neque consequuntur fugit Lorem, ipsum dolor sit amet consectetur adipisicing elit. Distinctio consectetur sit dolores, beatae error ipsum eos est quidem, soluta nobis, perferendis voluptatum? Dolor similique minus assumenda doloremque corrupti aliquam nulla. </ReadMore>
方案2:兼容JSX标签传入(适配你当前的调用写法)
修改组件逻辑,增加递归提取纯文本的方法,兼容任意合法React节点作为children,同时增加边界判断:文本总长度小于限制值时直接展示全量内容,不显示切换按钮:
import React, { useState, useMemo } from 'react'; const ReadMore = ({ limit, children }) => { const [isReadMore, setReadMore] = useState(false); // 递归提取任意React节点内的纯文本 const extractPlainText = (node) => { if (typeof node === 'string' || typeof node === 'number') return String(node); if (Array.isArray(node)) return node.map(extractPlainText).join(''); if (React.isValidElement(node)) return extractPlainText(node.props.children); return ''; }; const contentText = useMemo(() => extractPlainText(children), [children]); // 内容长度未超限时直接返回全量内容 if (contentText.length <= limit) { return <div>{children}</div> } const toggleReadMore = () => setReadMore(prev => !prev); return ( <div> {isReadMore ? children : `${contentText.substr(0, limit)}`} <button onClick={toggleReadMore} style={{ border: 'none', background: 'transparent', cursor: 'pointer', padding: '0 4px' }} > {isReadMore ? 'Réduire' : '... en savoir +'} </button> </div> ); }; export default ReadMore;
注意事项
如果需要在截断内容时保留原有的HTML标签样式,不能直接用纯文本截断的方案,否则会丢失标签格式,需要自行实现React节点遍历逻辑,按字符数累加截断到指定长度后停止,避免标签被截断出现渲染异常。绝大多数普通文本展示场景下,上述纯文本截断方案已经可以满足需求。
内容的提问来源于stack exchange,提问作者emerson
相关产品推荐
相关产品推荐

