完整示例
以下示例对齐仓库演示页 src/features/demo/office-preview-page.tsx。
React 单实例页面
"use client";
import { memo, useEffect, useRef, useState } from "react";
import {
ONLYOFFICE_CONTAINER_CONFIG,
ONLYOFFICE_ID,
ONLYOFFICE_LANG_KEY,
OnlyOfficeManager,
FILE_TYPE,
type FileType,
} from "@/components/onlyoffice-web-comp";
const OnlyOfficeHost = memo(function OnlyOfficeHost() {
return (
<div
className={`${ONLYOFFICE_CONTAINER_CONFIG.PARENT_CLASS_NAME} absolute inset-0`}
>
<div id={ONLYOFFICE_ID} className="absolute inset-0" />
</div>
);
});
async function fetchPublicFile(url: string, fileName: string) {
const response = await fetch(url);
if (!response.ok) throw new Error(`Failed to fetch ${url}`);
const blob = await response.blob();
return new File([blob], fileName, { type: blob.type });
}
export default function EditorPage({
defaultFileName = "New_Document.docx",
fileType = FILE_TYPE.DOCX,
initialFileUrl,
}: {
defaultFileName?: string;
fileType?: FileType;
initialFileUrl?: string;
}) {
const fileInputRef = useRef<HTMLInputElement>(null);
const managerRef = useRef<OnlyOfficeManager | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [readOnly, setReadOnly] = useState(false);
const [editorReady, setEditorReady] = useState(false);
useEffect(() => {
let unsubscribeLoading: (() => void) | undefined;
let cancelled = false;
const init = async () => {
let manager: OnlyOfficeManager;
if (initialFileUrl) {
const file = await fetchPublicFile(initialFileUrl, defaultFileName);
if (cancelled) return;
manager = await OnlyOfficeManager.createWithFile(
{ containerId: ONLYOFFICE_ID, fileType, defaultFileName, readOnly },
file,
);
} else {
manager = await OnlyOfficeManager.create({
containerId: ONLYOFFICE_ID,
fileType,
defaultFileName,
readOnly,
});
}
if (cancelled) {
manager.destroy();
return;
}
managerRef.current = manager;
setEditorReady(true);
unsubscribeLoading = manager.onLoadingChange(({ loading: next }) => {
setLoading(next);
});
};
init().catch(() => setError("无法加载编辑器组件"));
return () => {
cancelled = true;
unsubscribeLoading?.();
managerRef.current?.destroy();
managerRef.current = null;
};
}, []);
const openDocument = async (fileName: string, file?: File) => {
const manager = managerRef.current;
if (!manager) return;
await manager.openDocument({ fileName, file, readOnly });
};
return (
<div className="flex h-screen flex-col">
<div className="flex gap-2 p-4">
<button type="button" onClick={() => fileInputRef.current?.click()}>
上传文档
</button>
<button type="button" onClick={() => openDocument(defaultFileName)}>
新建文档
</button>
{editorReady && (
<>
<button type="button" onClick={() => managerRef.current?.downloadExport()}>
导出
</button>
<button
type="button"
onClick={() => {
managerRef.current?.toggleReadOnly();
setReadOnly(managerRef.current?.getReadOnly() ?? false);
}}
>
{readOnly ? "只读" : "编辑"}
</button>
</>
)}
</div>
{error && <p className="px-4 text-red-600">{error}</p>}
<div className="relative flex-1">
<OnlyOfficeHost />
{loading && <div className="absolute inset-0 bg-white/70" />}
</div>
<input
ref={fileInputRef}
type="file"
className="hidden"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) openDocument(file.name, file);
}}
/>
</div>
);
}
多实例 Tab(onlyOfficeManagerFactory)
import {
onlyOfficeManagerFactory,
FILE_TYPE,
} from "@/components/onlyoffice-web-comp";
// 按 containerId 打开,切换 Tab 时保留实例(隐藏而非销毁)
await onlyOfficeManagerFactory.open(
{
containerId: "tab-editor-abc",
fileType: FILE_TYPE.DOCX,
defaultFileName: "New_Document.docx",
},
{ fileName: "New_Document.docx", isNew: true },
);
// 页面卸载
onlyOfficeManagerFactory.destroyAll();
完整实现见 src/features/demo/tabs-multi-page.tsx。
何时用底层 API
以下场景可直接使用 EditorManager / createEditorView:
- 需要
url+ 自定义loader打开远程文档 - 批注修订、Word SDK 回调(
subscribe)
一般业务集成优先 OnlyOfficeManager,见 核心 API。