SPFx React应用报错:Cannot read properties of undefined (reading 'web')
问题:SPFx应用切换页面返回时抛出"Cannot read properties of undefined (reading 'web')"错误
问题描述
首次进入页面时SPFx应用运行正常,但切换到其他页面再返回时,抛出错误:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'web')
问题根源
- 全局SP实例未重置:PnPjs配置中的
_sp是全局单例,页面切换时WebPart被销毁,原有上下文失效,但单例未被重置,返回的是绑定了失效上下文的SP实例。 - 组件内SP实例初始化时机问题:React组件中
_sp变量通过空依赖的useEffect初始化,页面返回组件重新挂载时,第二个useEffect(依赖processName和selectedClient)可能先执行,此时_sp尚未完成初始化,导致调用_sp.web时出错。
修复方案
方案1:修改PnPjs配置,确保每次获取有效SP实例
调整pnpjsConfig.ts,允许传入上下文时强制更新实例,同时增加初始化校验:
import { WebPartContext } from "@microsoft/sp-webpart-base"; import { SPFI, SPFx, spfi } from "@pnp/sp"; import { LogLevel, PnPLogging } from "@pnp/logging"; import "@pnp/sp/web" import "@pnp/sp/webs"; import "@pnp/sp/lists"; import "@pnp/sp/items"; import "@pnp/sp/folders"; import "@pnp/sp/files"; import "@pnp/sp/files/folder"; import "@pnp/sp/batching"; import "@pnp/sp/attachments"; let _sp: SPFI | null = null; export const getSP = (context?: WebPartContext): SPFI => { // 传入上下文时强制初始化/更新实例,确保上下文有效 if (context != null) { _sp = spfi().using(SPFx(context)).using(PnPLogging(LogLevel.Warning)); } // 实例未初始化时抛出明确错误 if (_sp === null) { throw new Error("SP instance not initialized, please provide WebPartContext first"); } return _sp; }
方案2:在React组件中直接使用有效上下文初始化SP实例
移除组件内延迟初始化逻辑,直接通过props.context获取SP实例,确保每次组件挂载都使用当前有效上下文:
const HelloWorld = (props: IHelloWorldProps) => { // 直接通过传入的上下文初始化SP实例 const _sp = getSP(props.context); const [processName, setProcessName] = React.useState<string>(''); const [clients, setClients] = React.useState<Array<{ Title: string, Id: string }>>([]); const [processes, setProcesses] = React.useState<Array<{ Title: string, Id: string }>>([]); const [allDocuments, setAllDocuments] = React.useState<Array<IDocumentItem>>([]); const [selectedClient, setSelectedClient] = React.useState<number>(); React.useEffect(() => { const urlParams = new URLSearchParams(window.location.search); const myParam = urlParams.get('process'); if (myParam) setProcessName(myParam); }, []) React.useEffect(() => { const fetchClients = async () => { const items = await _sp.web.lists.getByTitle("Klienci").items(); setClients(items); const processList = await _sp.web.lists.getByTitle("Procesy").items(); setProcesses(processList); const docs = await _sp.web.lists.getByTitle("Documents").items(); const files = await _sp.web.getFolderByServerRelativePath("Documents").files(); const filesTransformed = await Promise.all(files.map(async e => { const item = await _sp.web.getFileByServerRelativePath(e.ServerRelativeUrl).getItem<{ Id: number, Title: string }>("Id", "Title"); return { id: item.Id, name: e.Name, path: `${e.ServerRelativeUrl}`, } })); const documents = docs.map((doc, index) => { return { id: doc.Id, name: filesTransformed.find(e => e.id === doc.Id)?.name || "", path: filesTransformed.find(e => e.id === doc.Id)?.path || "", clintsIds: doc.KlienciId, processesIds: doc.ProcesyId } }); const processId = processList.find(p => p.Title === processName)?.Id; const filteredDocuments = documents.filter(docu => docu.processesIds.includes(processId) && docu.clintsIds.includes(selectedClient)); setAllDocuments(filteredDocuments); } if (processName) { fetchClients(); } }, [processName, selectedClient, _sp]) // 其余组件代码保持不变 }
方案3:WebPart销毁时重置全局SP实例
在WebPart的onDispose方法中重置全局_sp变量,确保下次进入页面时重新初始化:
- 先修改
pnpjsConfig.ts导出_sp变量:
export let _sp: SPFI | null = null;
- 在WebPart类中更新
onDispose方法:
protected onDispose(): void { ReactDom.unmountComponentAtNode(this.domElement); // 重置全局SP实例 import("../../pnpjsConfig").then(module => { module._sp = null; }); }
验证
修改完成后重新打包部署SPFx应用,切换页面再返回,确认错误不再出现,数据能正常加载。
内容的提问来源于stack exchange,提问作者megabolo
相关产品推荐
相关产品推荐

