使用react-read-more-read-less加载数据库数据时遇substr报错
问题解决思路与方案
核心问题分析
children.substr is not a function报错原因:react-read-more-read-less组件要求子元素必须是字符串,但parse(inst.instructorBio)返回的是React元素对象(HTML转成的JSX节点),不是字符串,所以组件调用substr方法时直接报错。- 所有讲师同时展开的问题:你之前写的
toggleShow函数把展开状态存在了父组件的单一变量里,所有讲师共用同一个状态,自然会一点击全展开。
方案1:自定义截断逻辑+独立展开状态(推荐)
直接抛弃第三方组件的限制,自己实现逻辑,既解决报错,又保证每个讲师独立控制展开/收起。
步骤1:写一个HTML转纯文本的工具函数
用来判断简介内容长度是否超过300字符:
const getPlainTextFromHtml = (html) => { const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; return tempDiv.textContent || tempDiv.innerText || ''; };
步骤2:在父组件维护独立的展开状态
用对象存储每个讲师的展开状态,键为讲师ID:
import { useState } from 'react'; import parse from 'html-react-parser'; const InstructorList = ({ instructors }) => { // 存储每个讲师的展开状态:{ 讲师ID: 是否展开 } const [expandedMap, setExpandedMap] = useState({}); // 切换单个讲师的展开状态 const toggleExpand = (id) => { setExpandedMap(prev => ({ ...prev, [id]: !prev[id] })); }; return ( <div className="instructor-list"> {instructors.map(inst => { const plainText = getPlainTextFromHtml(inst.instructorBio); const isLong = plainText.length > 300; const isExpanded = expandedMap[inst.id] || false; // 渲染内容:展开显示完整HTML,收起显示截断后的纯文本转HTML const displayContent = isExpanded ? parse(inst.instructorBio) : parse(`<p>${plainText.slice(0, 300)}...</p>`); return ( <div key={inst.id} className="instructor-card"> <img src={inst.instructorImage} alt={inst.instructorName} /> <h3>{inst.instructorName}</h3> <div className="bio">{displayContent}</div> {isLong && ( <button onClick={() => toggleExpand(inst.id)} className="read-more-btn" > {isExpanded ? 'Read Less' : 'Read More'} </button> )} </div> ); })} </div> ); };
方案2:适配react-read-more-read-less组件
如果一定要用这个第三方组件,就给它传纯文本字符串,同时把每个讲师抽成独立组件维护自己的展开状态:
步骤1:抽离独立的讲师卡片组件
import { useState } from 'react'; import parse from 'html-react-parser'; import ReactReadMoreReadLess from 'react-read-more-read-less'; const InstructorCard = ({ inst }) => { const [isExpanded, setIsExpanded] = useState(false); const plainText = getPlainTextFromHtml(inst.instructorBio); const isLong = plainText.length > 300; return ( <div className="instructor-card"> <img src={inst.instructorImage} alt={inst.instructorName} /> <h3>{inst.instructorName}</h3> {!isExpanded ? ( <ReactReadMoreReadLess readMoreClassName="readMoreClassName" charLimit={300} readMoreText="Read More" onClick={() => setIsExpanded(true)} > {plainText} </ReactReadMoreReadLess> ) : ( <> {parse(inst.instructorBio)} <button onClick={() => setIsExpanded(false)} className="readMoreClassName" > Read Less </button> </> )} </div> ); };
步骤2:父组件中使用该组件
const InstructorList = ({ instructors }) => { return ( <div className="instructor-list"> {instructors.map(inst => ( <InstructorCard key={inst.id} inst={inst} /> ))} </div> ); };
内容的提问来源于stack exchange,提问作者Stephen Scott Moore
相关产品推荐
相关产品推荐

