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

使用react-read-more-read-less加载数据库数据时遇substr报错

问题解决思路与方案

核心问题分析

  1. children.substr is not a function报错原因:react-read-more-read-less组件要求子元素必须是字符串,但parse(inst.instructorBio)返回的是React元素对象(HTML转成的JSX节点),不是字符串,所以组件调用substr方法时直接报错。
  2. 所有讲师同时展开的问题:你之前写的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 17:25:19