SPFx React应用刷新/预览时出现TypeError: Cannot read 'web'属性求助
问题:SPFx应用刷新/预览时抛出"Cannot read properties of undefined (reading 'web')"错误
我基于React开发了SPFx应用,首次进入页面时运行正常,但刷新页面或进入预览模式时,会抛出错误:
caught (in promise) TypeError: Cannot read properties of undefined (reading 'web')
相关代码
package.json
{ "name": "bms-next-gen-spfx", "version": "0.0.1", "private": true, "engines": { "node": ">=18.17.1 <19.0.0" }, "main": "lib/index.js", "scripts": { "build": "gulp bundle", "clean": "gulp clean", "test": "gulp test" }, "dependencies": { "@fluentui/react": "^8.106.4", "@fluentui/react-components": "^9.52.0", "@fluentui/react-list-preview": "^0.2.6", "@fluentui/react-portal-compat": "^9.0.146", "@microsoft/sp-component-base": "1.19.0", "@microsoft/sp-core-library": "1.19.0", "@microsoft/sp-lodash-subset": "1.19.0", "@microsoft/sp-office-ui-fabric-core": "1.19.0", "@microsoft/sp-property-pane": "1.19.0", "@microsoft/sp-webpart-base": "1.19.0", "@pnp/graph": "^4.13.0", "@pnp/logging": "^4.13.0", "@pnp/sp": "^4.13.0", "@pnp/spfx-controls-react": "3.21.0", "@pnp/spfx-property-controls": "3.20.0", "react": "17.0.1", "react-dom": "17.0.1", "tslib": "2.3.1" }, "devDependencies": { "@microsoft/eslint-config-spfx": "1.20.1", "@microsoft/eslint-plugin-spfx": "1.20.1", "@microsoft/rush-stack-compiler-4.7": "0.1.0", "@microsoft/sp-build-web": "1.20.1", "@microsoft/sp-module-interfaces": "1.20.1", "@rushstack/eslint-config": "2.5.1", "@types/react": "17.0.45", "@types/react-dom": "17.0.17", "@types/webpack-env": "~1.15.2", "ajv": "^6.12.5", "eslint": "8.7.0", "eslint-plugin-react-hooks": "4.3.0", "gulp": "4.0.2", "typescript": "4.7.4" } }
PnPJS配置文件
import { WebPartContext } from "@microsoft/sp-webpart-base"; import { spfi, SPFI, SPFx } from "@pnp/sp"; import { PnPLogging, LogLevel } from "@pnp/logging"; import "@pnp/sp/webs"; import "@pnp/sp/lists"; import "@pnp/sp/fields"; import "@pnp/sp/lists/web"; import "@pnp/sp/items"; import "@pnp/sp/site-users/web"; import "@pnp/sp/presets/all"; let _sp: SPFI; export const getSP = (context?: WebPartContext): SPFI => { if(context != null) { _sp = spfi().using(SPFx(context)).using(PnPLogging(LogLevel.Warning)) } return _sp! }
Navigation组件代码
import * as React from 'react'; import type { INavigationProps } from './INavigationProps'; import { SPFI } from '@pnp/sp'; import { useEffect, useState } from 'react'; import { INavigation } from '../../../interface'; import { getSP } from '../../../pnpjsConfig'; import styles from './Navigation.module.scss'; import { ComboBoxListItemPicker } from '@pnp/spfx-controls-react/lib/ListItemPicker'; import type { ComboboxProps } from "@fluentui/react-components"; import { IReadonlyTheme } from '@microsoft/sp-component-base'; import { Combobox, Option, useId, Field } from "@fluentui/react-components"; //import { Accordion } from "@pnp/spfx-controls-react/lib/Accordion"; const Navigation = (props:INavigationProps) => { const LOG_SOURCE = "Navigation Webpart"; const LIST_NAME = "Activite"; const _sp:SPFI = getSP(props.context); const[activiteItems,setActiviteItems] = useState<INavigation[]>([]); const [title, setTitle] = useState(''); const [status, setStatus] = useState(''); const [ListId, setListId] = useState(''); const { semanticColors }: IReadonlyTheme= props.themeVariant!; const fetchData = async () => { if (!_sp) { console.error('SP context is undefined'); } const list_data= await _sp.web.lists.getByTitle(LIST_NAME).select("Id")(); setListId(list_data.Id); console.log("List Id ", list_data.Id); console.log('context',_sp); const items = await _sp.web.lists.getByTitle(LIST_NAME).items.select("Id","Title","Livrable/Id","Livrable/Title","Fonction/Id","Fonction/Title","Process/Id","Process/Title").expand("Livrable","Fonction","Process")(); console.log('Activite items',items); setActiviteItems((items).map((item:any) => { return { Id: item.Id, Title: item.Title, LivrableId: item.Livrable.Id, LivrableTitle: item.Livrable.Title, ProcessId: item.Process.Id, ProcessTitle: item.Process.Title, Fonction: item.Fonction.map((f:any) => ({ Id: f.Id, Title: f.Title }) ) }; })); } useEffect(() => { fetchData();[props] });
问题原因及解决方案
1. PnP配置的空值断言风险
当前getSP函数仅在传入context时初始化_sp,但刷新/预览时组件重新渲染可能出现props.context未及时传入的情况,此时_sp仍为undefined,但函数通过_sp!强制断言非空,导致调用_sp.web时触发错误。
修复getSP函数:
export const getSP = (context?: WebPartContext): SPFI => { if (context) { _sp = spfi().using(SPFx(context)).using(PnPLogging(LogLevel.Warning)); } if (!_sp) { throw new Error("SPFI instance not initialized. Ensure context is provided on first call."); } return _sp; }
2. useEffect依赖数组语法错误
当前useEffect的依赖数组写在了函数调用内部,属于语法错误,导致useEffect每次渲染都会执行,且可能在_sp未初始化时调用fetchData。
修复useEffect:
useEffect(() => { if (_sp) { // 确保_sp初始化后再执行数据请求 fetchData(); } }, [props, _sp]); // 正确设置依赖数组
3. fetchData空值检查不彻底
当前fetchData中仅打印_sp未定义的日志,但仍继续执行后续代码,直接调用_sp.web导致报错。
修复fetchData函数:
const fetchData = async () => { if (!_sp) { console.error('SP context is undefined'); return; // 提前返回,避免后续报错 } try { const list_data= await _sp.web.lists.getByTitle(LIST_NAME).select("Id")(); setListId(list_data.Id); console.log("List Id ", list_data.Id); console.log('context',_sp); const items = await _sp.web.lists.getByTitle(LIST_NAME).items.select("Id","Title","Livrable/Id","Livrable/Title","Fonction/Id","Fonction/Title","Process/Id","Process/Title").expand("Livrable","Fonction","Process")(); console.log('Activite items',items); setActiviteItems((items).map((item:any) => { return { Id: item.Id, Title: item.Title, LivrableId: item.Livrable.Id, LivrableTitle: item.Livrable.Title, ProcessId: item.Process.Id, ProcessTitle: item.Process.Title, Fonction: item.Fonction.map((f:any) => ({ Id: f.Id, Title: f.Title }) ) }; })); } catch (error) { console.error(`${LOG_SOURCE} - Error fetching data:`, error); } }
4. 确认WebPart正确传递context
检查WebPart的render方法,确保传递给Navigation组件的context正确:
public render(): void { const element: React.ReactElement<INavigationProps> = React.createElement( Navigation, { context: this.context, themeVariant: this.props.themeVariant } ); ReactDom.render(element, this.domElement); }
内容的提问来源于stack exchange,提问作者Timaz69
相关产品推荐
相关产品推荐

