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

Draft.js装饰器实现超1000字文本标背景时换行失效问题求助

问题根因

Draft.js 的 CompositeDecorator 会逐块处理内容(每次回车换行都会生成一个独立的 ContentBlock),你当前的策略逻辑仅判断单个块的字符长度,没有统计所有块的累计总长度,因此换行后功能异常。

解决方案

核心逻辑为每次编辑器内容变更时,先统计所有块的全局累计偏移量和总字符数,再基于全局值判断每个块内超出1000字的范围,生成对应装饰器。修改后的完整代码如下:

import React, { useState, useRef, useCallback } from "react";
import {
  Editor,
  EditorState,
  ContentState,
  CompositeDecorator,
  convertToRaw,
} from "draft-js";

const styles = {
  editor: {
    border: "1px solid gray",
    minHeight: "6em",
    padding: "8px"
  },
};

// 统计所有块的全局起始偏移量和总字符数
const getBlockOffsets = (contentState) => {
  const blocks = contentState.getBlocksAsArray();
  let currentOffset = 0;
  const blockOffsetMap = new Map();

  blocks.forEach((block) => {
    blockOffsetMap.set(block.getKey(), currentOffset);
    // 每个块末尾加1个换行符长度,和提交时的拼接逻辑对齐
    currentOffset += block.getText().length + 1;
  });

  return {
    blockOffsetMap,
    // 减掉最后多算的1个换行符长度
    totalLength: currentOffset > 0 ? currentOffset - 1 : 0
  };
};

// 根据全局偏移和总长度生成装饰器
const createDecorator = (blockOffsetMap, totalLength) => {
  // 超出部分的高亮组件
  const OverLengthComponent = ({ children }) => {
    return <span style={{ backgroundColor: "#fecdd3" }}>{children}</span>;
  };

  // 块处理策略
  const blockStrategy = (contentBlock, callback) => {
    // 总长度未超出直接返回
    if (totalLength <= 1000) return;

    const blockKey = contentBlock.getKey();
    const blockStartGlobalOffset = blockOffsetMap.get(blockKey);
    const blockText = contentBlock.getText();
    const blockLength = blockText.length;

    // 计算当前块内超出1000字的区间
    const overStartInBlock = Math.max(1000 - blockStartGlobalOffset, 0);
    const overEndInBlock = blockLength;

    if (overStartInBlock < overEndInBlock) {
      callback(overStartInBlock, overEndInBlock);
    }
  };

  return new CompositeDecorator([
    {
      strategy: blockStrategy,
      component: OverLengthComponent,
    },
  ]);
};

function TextArea(props) {
  const [editorState, setEditorState] = useState(() => {
    // 初始化时计算偏移生成装饰器
    const initialContent = ContentState.createFromText(props.value || "");
    const { blockOffsetMap, totalLength } = getBlockOffsets(initialContent);
    const initialDecorator = createDecorator(blockOffsetMap, totalLength);
    return EditorState.createWithContent(initialContent, initialDecorator);
  });

  const editor = useRef(null);

  const focusEditor = useCallback(() => {
    editor.current?.focus();
  }, []);

  const onChange = useCallback((newEditorState) => {
    const contentState = newEditorState.getCurrentContent();
    // 计算最新的全局偏移和总长度
    const { blockOffsetMap, totalLength } = getBlockOffsets(contentState);
    // 生成新装饰器
    const newDecorator = createDecorator(blockOffsetMap, totalLength);
    // 更新editorState的装饰器
    const finalEditorState = EditorState.set(newEditorState, {
      decorator: newDecorator
    });

    // 原有提交逻辑
    const rawBlocks = convertToRaw(contentState).blocks;
    const value = rawBlocks
      .map((block) => !block.text.trim() ? "\n" : block.text)
      .join("\n");
      
    setEditorState(finalEditorState);
    props.formik?.setFieldValue(props.name, value === "\n" ? "" : value);
  }, [props]);

  React.useEffect(() => {
    focusEditor();
  }, [focusEditor]);

  return (
    <div style={styles.editor} onClick={focusEditor}>
      <Editor
        ref={editor}
        editorState={editorState}
        onChange={onChange}
        placeholder="请输入内容"
      />
    </div>
  );
}

export default TextArea;

内容的提问来源于stack exchange,提问作者stark_sanjeev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 02:54:04