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

React组件中ReactDom.render返回类型不匹配FC的问题如何解决?

React FC组件返回类型不匹配问题解决

问题背景

核心需求是实现Markdown渲染,同时解析传入的HTML格式字符串。已知children为HTML字符串,且isParseRequired参数设为true。使用TypeScript编写的Markdown组件出现TypeScript类型错误。

原组件代码

import cx from 'classnames'
import 'github-markdown-css'
import 'katex/dist/katex.min.css'
import { FC, ReactNode, useEffect, useMemo, useState } from 'react'
import { CopyToClipboard } from 'react-copy-to-clipboard'
import { BsClipboard } from 'react-icons/bs'
import ReactDom from 'react-dom'
import ReactMarkdown from 'react-markdown'
import reactNodeToString from 'react-node-to-string'
import rehypeHighlight from 'rehype-highlight'
import remarkBreaks from 'remark-breaks'
import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math'
import supersub from 'remark-supersub'
import Tooltip from '../Tooltip'
import './markdown.css'

const Markdown: FC<{ children: string, isParseRequired?: boolean}> = ({ children, isParseRequired = false }) => {
    return ReactDom.render(
      <ReactMarkdown
        remarkPlugins={[remarkMath, supersub, remarkBreaks, remarkGfm]}
        rehypePlugins={[[rehypeHighlight, { detect: true, ignoreMissing: true }]]}
        className={`markdown-body markdown-custom-styles !text-base font-normal`}
        linkTarget="_blank"
        components={{
          a: ({ node, ...props }) => {
            if (!props.title) {
              return <a {...props} />
            }
            return (
              <Tooltip content={props.title}>
                <a {...props} title={undefined} />
              </Tooltip>
            )
          },
          code: ({ node, inline, className, children, ...props }) => {
            if (inline) {
                return (
                  <code className={className} {...props}>
                    {children}
                  </code>
                )
            }
            return <CustomCode className={className}>{children}</CustomCode>
          },
        }}
        children={children}/>,
        document.body)
}

export default Markdown

错误信息

src/app/components/Markdown/index.tsx:48:7 - error TS2322: Type '({ children, isParseRequired }: { children: string; isParseRequired?: boolean | undefined; }) => void | Element' is not assignable to type 'FC<{ children: string; isParseRequired?: boolean | undefined; }>'. 

问题原因

React函数组件(FC)要求返回ReactNode类型的JSX元素,而ReactDom.render的返回值是渲染出的DOM元素或void,与FC的返回类型不匹配。另外,ReactDom.render是用于将React应用挂载到根DOM节点的方法,不能在组件内部用来生成返回内容。

解决方法

修改后的组件代码

import cx from 'classnames'
import 'github-markdown-css'
import 'katex/dist/katex.min.css'
import { FC, ReactNode, useMemo } from 'react'
import { CopyToClipboard } from 'react-copy-to-clipboard'
import { BsClipboard } from 'react-icons/bs'
import ReactMarkdown from 'react-markdown'
import reactNodeToString from 'react-node-to-string'
import rehypeHighlight from 'rehype-highlight'
import remarkBreaks from 'remark-breaks'
import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math'
import supersub from 'remark-supersub'
import Tooltip from '../Tooltip'
import './markdown.css'

// 补充原代码中缺失的CustomCode组件
const CustomCode: FC<{ className?: string; children: ReactNode }> = ({ className, children }) => {
  return (
    <div className="code-block-wrapper">
      <CopyToClipboard text={String(children)}>
        <button className="copy-button">
          <BsClipboard />
        </button>
      </CopyToClipboard>
      <pre className={className}>
        <code>{children}</code>
      </pre>
    </div>
  )
}

const Markdown: FC<{ children: string; isParseRequired?: boolean }> = ({ children, isParseRequired = false }) => {
  // 根据isParseRequired动态配置rehype插件,添加HTML解析能力
  const rehypePlugins = useMemo(() => {
    const basePlugins = [[rehypeHighlight, { detect: true, ignoreMissing: true }]]
    if (isParseRequired) {
      // 需先安装依赖:npm install rehype-raw
      const rehypeRaw = require('rehype-raw').default
      basePlugins.push(rehypeRaw)
    }
    return basePlugins
  }, [isParseRequired])

  return (
    <ReactMarkdown
      remarkPlugins={[remarkMath, supersub, remarkBreaks, remarkGfm]}
      rehypePlugins={rehypePlugins}
      className={`markdown-body markdown-custom-styles !text-base font-normal`}
      linkTarget="_blank"
      components={{
        a: ({ node, ...props }) => {
          if (!props.title) {
            return <a {...props} />
          }
          return (
            <Tooltip content={props.title}>
              <a {...props} title={undefined} />
            </Tooltip>
          )
        },
        code: ({ node, inline, className, children, ...props }) => {
          if (inline) {
            return (
              <code className={className} {...props}>
                {children}
              </code>
            )
          }
          return <CustomCode className={className}>{children}</CustomCode>
        },
      }}
      children={children}
    />
  )
}

export default Markdown

关键修改点

  • 移除组件内部的ReactDom.render调用,直接返回<ReactMarkdown>JSX元素,符合FC组件的返回类型要求
  • 补充原代码中未定义的CustomCode组件,避免运行时错误
  • 新增isParseRequired逻辑:当需要解析HTML时,引入rehype-raw插件(需先执行npm install rehype-raw安装依赖)
  • 使用useMemo缓存插件数组,避免组件每次渲染时重复创建插件列表,优化性能

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 00:43:10