SPFx React - 错误无法读取未定义的属性(读取‘web’)
2024-01-26
459
我使用 React 制作了应用程序 SPFx。当我进入应用程序所在的页面时,它可以正常工作。但是当我返回或转到另一个页面,然后返回到包含应用程序的页面时,我收到错误:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'web')
我的代码:
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
type IPropertyPaneConfiguration,
PropertyPaneTextField,
PropertyPaneCheckbox,
PropertyPaneDropdown,
PropertyPaneToggle
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { IReadonlyTheme } from '@microsoft/sp-component-base';
import * as strings from 'HelloWorldWebPartStrings';
import HelloWorld from './components/HelloWorld';
import { IHelloWorldProps } from './components/IHelloWorldProps';
import { spfi, SPFx } from "@pnp/sp";
import { getSP } from '../../pnpjsConfig';
export interface IHelloWorldWebPartProps {
description: string;
}
export default class HelloWorldWebPart extends BaseClientSideWebPart<IHelloWorldWebPartProps> {
private _isDarkTheme: boolean = false;
private _environmentMessage: string = '';
public render(): void {
const element: React.ReactElement<IHelloWorldProps> = React.createElement(
HelloWorld,
{
description: this.properties.description,
isDarkTheme: this._isDarkTheme,
environmentMessage: this._environmentMessage,
hasTeamsContext: !!this.context.sdks.microsoftTeams,
userDisplayName: this.context.pageContext.user.displayName,
context: this.context,
}
);
ReactDom.render(element, this.domElement);
}
protected async onInit(): Promise<void> {
this._environmentMessage = await this._getEnvironmentMessage();
super.onInit();
getSP(this.context);
}
private _getEnvironmentMessage(): Promise<string> {
if (!!this.context.sdks.microsoftTeams) { // running in Teams, office.com or Outlook
return this.context.sdks.microsoftTeams.teamsJs.app.getContext()
.then(context => {
let environmentMessage: string = '';
switch (context.app.host.name) {
case 'Office': // running in Office
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOffice : strings.AppOfficeEnvironment;
break;
case 'Outlook': // running in Outlook
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOutlook : strings.AppOutlookEnvironment;
break;
case 'Teams': // running in Teams
case 'TeamsModern':
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentTeams : strings.AppTeamsTabEnvironment;
break;
default:
environmentMessage = strings.UnknownEnvironment;
}
return environmentMessage;
});
}
return Promise.resolve(this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentSharePoint : strings.AppSharePointEnvironment);
}
protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {
if (!currentTheme) {
return;
}
this._isDarkTheme = !!currentTheme.isInverted;
const {
semanticColors
} = currentTheme;
if (semanticColors) {
this.domElement.style.setProperty('--bodyText', semanticColors.bodyText || null);
this.domElement.style.setProperty('--link', semanticColors.link || null);
this.domElement.style.setProperty('--linkHovered', semanticColors.linkHovered || null);
}
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
})
]
}
]
}
]
};
}
}
pnpjs:
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 (_sp === null && context != null) {
_sp = spfi().using(SPFx(context)).using(PnPLogging(LogLevel.Warning))
}
return _sp as SPFI;
}
组件:
import * as React from 'react';
import type { IHelloWorldProps } from './IHelloWorldProps';
import { SPFI } from '@pnp/sp';
import { IDocumentItem } from '../../../interfaces';
import { getSP } from '../../../pnpjsConfig';
import ClientSelect from './ClientSelectProps';
import { ListView, IViewField, SelectionMode } from "@pnp/spfx-controls-react";
import { IItem } from "@pnp/sp/items/types";
import { IAttachmentInfo } from "@pnp/sp/attachments";
const HelloWorld = (props: IHelloWorldProps) => {
// let _sp: SPFI = getSP(props.context) as SPFI;
let _sp: SPFI;
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(() => {
_sp = getSP() as SPFI;
}, [])
React.useEffect(() => {
const urlParams = new URLSearchParams(window.location.search);
const myParam = urlParams.get('process');
if (myParam) setProcessName(myParam);
const fetchClients = async () => {
console.log("TEST---------------", _sp.web);
const items = await _sp.web.lists.getByTitle("Klienci").items();
setClients(items);
console.log({ items });
const processList = await _sp.web.lists.getByTitle("Procesy").items();
setProcesses(processList);
console.log({ 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}`,
}
}));
console.log({ docs });
console.log({ files });
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 === myParam)?.Id;
console.log({ selectedClient });
console.log({ processId })
const filteredDocuments = documents.filter(docu => docu.processesIds.includes(processId) && docu.clintsIds.includes(selectedClient));
console.log({ filteredDocuments });
setAllDocuments(filteredDocuments);
}
if (!_sp) {
_sp = getSP(props.context) as SPFI;
}
if (processName && _sp) {
fetchClients();
}
}, [processName, selectedClient])
const viewFields: IViewField[] = [
{
name: 'id',
displayName: 'ID',
sorting: true,
maxWidth: 70,
},
{
name: 'name',
displayName: 'Nazwa',
sorting: true,
maxWidth: 70,
},
{
name: 'path',
displayName: 'Link do dokumentu',
sorting: true,
maxWidth: 200,
render: (item: any) => {
return <a href={item['path']} target="_blank" rel="noopener noreferrer">Otwórz dokument</a>;
}
}
];
return (
<>
<h1>{processName}</h1>
<ClientSelect
clients={clients}
onChange={(selectedClientId) => {
console.log("Wybrano klienta o ID:", selectedClientId);
setSelectedClient(parseInt(selectedClientId));
}}
/>
<ListView
items={allDocuments}
viewFields={viewFields}
iconFieldName="ServerRelativeUrl"
compact={true}
selectionMode={SelectionMode.multiple}
showFilter={true}
defaultFilter=""
filterPlaceHolder="Search..."
stickyHeader={true}
/>
</>
)
}
export default HelloWorld;
我尝试将日志放入代码中,更改使用上下文的方式等。这就像丢失了上下文,但根据我的代码和我的理解,它不应该如此。
2个回答
看起来错误已经解决。相关问题单为 https://github.com/SharePoint/sp-dev-docs/issues/8795
Eiglimar Junior
2024-04-02
只需将您的 pnp 类更改为并重试:
import { WebPartContext } from "@microsoft/sp-webpart-base";
// import pnp and pnp logging system you can insert the log if you need
import { spfi, SPFI, SPFx } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
import "@pnp/sp/batching";
import "@pnp/sp/items/get-all";
import "@pnp/sp/attachments";
import "@pnp/sp/site-users/web"
let _sp: SPFI;
export const getSP = (context?: WebPartContext): SPFI => {
// eslint-disable-next-line eqeqeq
if (context != null) { // eslint-disable-line eqeqeq
// eslint-disable-next-line @rushstack/no-new-null
//You must add the @pnp/logging package to include the PnPLogging behavior it is no longer a peer dependency
// The LogLevel set's at what level a message will be written to the console
_sp = spfi().using(SPFx(context))
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return _sp!;
};
我已经复制了您的代码,其他一切都应该可以正常工作
Andrés
2024-05-21