mirror of
https://github.com/pnp/sp-dev-fx-webparts.git
synced 2025-03-27 10:28:36 +00:00
136 lines
4.6 KiB
TypeScript
136 lines
4.6 KiB
TypeScript
import { Logger, LogLevel } from "@pnp/logging";
|
|
import { spfi, SPFI } from "@pnp/sp";
|
|
import { useEffect, useState } from "react";
|
|
import { IFile, IResponseItem } from "../components/interfaces";
|
|
import { getSP } from "../pnpjsConfig";
|
|
import { Caching } from "@pnp/queryable";
|
|
import { IItemUpdateResult } from "@pnp/sp/items";
|
|
|
|
const useDocuments = () => {
|
|
const LOG_SOURCE = "🅿PnPjsExample";
|
|
const LIBRARY_NAME = "Documents";
|
|
|
|
const [documents, setDocuments] = useState<IFile[]>([]);
|
|
const [totalSize, setTotalSize] = useState<number>(0);
|
|
const [isError, setError] = useState<Boolean>(false);
|
|
|
|
const _sp: SPFI = getSP();
|
|
|
|
//side effect with empty depdency array, means to run once
|
|
useEffect(() => {
|
|
//mount logic to prevent updates to state when unmounted. Typically we'd use AbortController with Fetch, just can't with PnPjs
|
|
let mounted = true;
|
|
(async () => {
|
|
try {
|
|
// do PnP JS query, some notes:
|
|
// - .expand() method will retrive Item.File item but only Length property
|
|
// - .get() always returns a promise
|
|
// - await resolves proimises making your code act syncronous, ergo Promise<IResponseItem[]> becomes IResponse[]
|
|
|
|
//Extending our sp object to include caching behavior, this modification will add caching to the sp object itself
|
|
//this._sp.using(Caching("session"));
|
|
|
|
//Creating a new sp object to include caching behavior. This way our original object is unchanged.
|
|
const spCache = spfi(_sp).using(Caching({ store: "session" }));
|
|
|
|
const response: IResponseItem[] = await spCache.web.lists
|
|
.getByTitle(LIBRARY_NAME)
|
|
.items.select("Id", "Title", "FileLeafRef", "File/Length")
|
|
.expand("File/Length")();
|
|
|
|
// use map to convert IResponseItem[] into our internal object IFile[]
|
|
const documents: IFile[] = response.map((item: IResponseItem) => {
|
|
return {
|
|
Id: item.Id,
|
|
Title: item.Title || "Unknown",
|
|
Size: item.File?.Length || 0,
|
|
Name: item.FileLeafRef,
|
|
};
|
|
});
|
|
|
|
//only update state if we are still mounted
|
|
if (mounted) {
|
|
// Add the items and totalsize to the state of the hook
|
|
setDocuments(documents);
|
|
setTotalSize(
|
|
documents.length > 0
|
|
? documents.reduce<number>((acc: number, item: IFile) => {
|
|
return acc + Number(item.Size);
|
|
}, 0)
|
|
: 0
|
|
);
|
|
}
|
|
} catch (err) {
|
|
if (mounted) {
|
|
setError(true);
|
|
}
|
|
Logger.write(
|
|
`${LOG_SOURCE} (getting files useEffect) - ${JSON.stringify(err)} - `,
|
|
LogLevel.Error
|
|
);
|
|
}
|
|
|
|
//best practice to return a cleanup method in scenarios where component unmounts before completion
|
|
return () => (mounted = false);
|
|
})();
|
|
}, []);
|
|
|
|
const updateDocuments = async () => {
|
|
//mount logic to prevent updates to state when unmounted. Typically we'd use AbortController with Fetch, just can't with PnPjs
|
|
let mounted = true;
|
|
|
|
try {
|
|
const [batchedSP, execute] = _sp.batched();
|
|
|
|
//clone documents
|
|
const items = JSON.parse(JSON.stringify(documents));
|
|
|
|
const res: IItemUpdateResult[] = [];
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
// you need to use .then syntax here as otherwise the application will stop and await the result
|
|
batchedSP.web.lists
|
|
.getByTitle(LIBRARY_NAME)
|
|
.items.getById(items[i].Id)
|
|
.update({ Title: `${items[i].Name}-Updated` })
|
|
.then((r) => res.push(r));
|
|
}
|
|
// Executes the batched calls
|
|
await execute();
|
|
|
|
// Results for all batched calls are available
|
|
for (let i = 0; i < res.length; i++) {
|
|
//If the result is successful update the item
|
|
//NOTE: This code is over simplified, you need to make sure the Id's match
|
|
const item = await res[i].item.select("Id, Title")<{
|
|
Id: number;
|
|
Title: string;
|
|
}>();
|
|
items[i].Name = item.Title;
|
|
}
|
|
|
|
//only update state if we are still mounted
|
|
if (mounted) {
|
|
//Update the state
|
|
setDocuments(items);
|
|
setError(false);
|
|
}
|
|
} catch (err) {
|
|
if (mounted) {
|
|
setError(true);
|
|
}
|
|
Logger.write(
|
|
`${LOG_SOURCE} (updating titles) - ${JSON.stringify(err)} - `,
|
|
LogLevel.Error
|
|
);
|
|
}
|
|
|
|
//best practice to return a cleanup method in scenarios where component unmounts before completion
|
|
return () => (mounted = false);
|
|
};
|
|
|
|
return [documents, updateDocuments, totalSize, isError] as const;
|
|
};
|
|
|
|
export default useDocuments;
|