Next.js 13.4中结合forwardRef与next/dynamic使用react-email-editor的正确方式
解决Next.js中react-email-editor的Ref指向问题
在Next.js中使用react-email-editor组件时,遇到了无法通过ref获取组件实例的问题,导致无法导出编辑器内容到剪贴板。虽已通过next/dynamic禁用SSR,但emailEditorRef始终无法正确指向react-email-editor实例。
环境版本
- react@18.2.0
- next@13.4.19
- react-email-editor@^1.7.9
现有代码结构
page.tsx
'use client'; import { Button } from '@/components/ui/button'; import { toast } from '@/components/ui/use-toast'; import React, { useEffect, useRef } from 'react'; import { EditorRef, EmailEditorProps } from '@/components/email-template/types'; import ForwardedEmailEditor from '@/components/email-template/email-ui'; export default function Page() { const emailEditorRef = useRef<EditorRef | null>(null); useEffect(() => { console.log('ref changed = ', emailEditorRef.current); }, [emailEditorRef]); const exportHtml = () => { const unlayer = emailEditorRef.current?.editor; unlayer?.exportHtml((data) => { const { design, html } = data; try { console.log('Copy function Running',html); navigator.clipboard.writeText(html); } catch (error) { console.error('Clipboard write failed:', error); } }); }; const onReady: EmailEditorProps['onReady'] = (unlayer) => { console.log('editor react-email-editor is ready', emailEditorRef.current); }; return ( <section > <div> <span>Create Email Template</span> <Button onClick={exportHtml}>Export HTML</Button> </div> <div> <ForwardedEmailEditor ref={emailEditorRef} onReady={onReady} /> </div> </section> ); }
email-ui.tsx
'use client'; import React from 'react'; import dynamic from 'next/dynamic'; import { EmailEditorProps, EditorRef } from './types'; const DynamicEmailEditor = dynamic(() => import('react-email-editor'), { ssr: false }); const ForwardedEmailEditor = React.forwardRef<EditorRef, EmailEditorProps>((props, ref) => { return <DynamicEmailEditor ref={ref} {...props} />; }); export default ForwardedEmailEditor;
types.tsx
'use client'; export type { EditorRef, EmailEditorProps } from 'react-email-editor';
解决方案
方案1:修复Ref转发逻辑
动态加载组件时,直接转发ref可能存在传递延迟或绑定问题,需在ForwardedEmailEditor中创建内部ref,并手动同步到外部ref:
修改email-ui.tsx:
'use client'; import React, { useEffect, useRef } from 'react'; import dynamic from 'next/dynamic'; import { EmailEditorProps, EditorRef } from './types'; const DynamicEmailEditor = dynamic(() => import('react-email-editor'), { ssr: false }); const ForwardedEmailEditor = React.forwardRef<EditorRef, EmailEditorProps>((props, ref) => { const editorRef = useRef<EditorRef>(null); // 同步内部ref到外部ref useEffect(() => { if (ref) { if (typeof ref === 'function') { ref(editorRef.current); } else { ref.current = editorRef.current; } } }, [ref]); return <DynamicEmailEditor ref={editorRef} {...props} />; }); export default ForwardedEmailEditor;
方案2:通过onReady回调保存实例(更可靠)
利用react-email-editor提供的onReady回调直接获取编辑器实例,无需依赖ref:
修改page.tsx:
'use client'; import { Button } from '@/components/ui/button'; import { toast } from '@/components/ui/use-toast'; import React, { useState } from 'react'; import { EmailEditorProps } from '@/components/email-template/types'; import ForwardedEmailEditor from '@/components/email-template/email-ui'; // 导入Unlayer类型(react-email-editor内部暴露的实例类型) import type { Unlayer } from 'react-email-editor'; export default function Page() { // 用状态保存编辑器实例 const [unlayerInstance, setUnlayerInstance] = useState<Unlayer | null>(null); const exportHtml = () => { unlayerInstance?.exportHtml((data) => { const { html } = data; try { navigator.clipboard.writeText(html); toast({ title: 'HTML已成功复制到剪贴板' }); } catch (error) { console.error('剪贴板写入失败:', error); toast({ title: '复制失败,请重试', variant: 'destructive' }); } }); }; const onReady: EmailEditorProps['onReady'] = (unlayer) => { setUnlayerInstance(unlayer); }; return ( <section> <div> <span>Create Email Template</span> <Button onClick={exportHtml}>Export HTML</Button> </div> <div> <ForwardedEmailEditor onReady={onReady} /> </div> </section> ); }
说明
方案2无需处理ref的转发问题,直接通过组件提供的回调获取实例,是更稳定的实现方式。两种方案均可解决无法获取编辑器实例的问题,根据实际需求选择即可。
内容的提问来源于stack exchange,提问作者kavandalal
相关产品推荐
相关产品推荐

