Question

I'm trying to create a modern webpart that connects to MSGraphClient using the UI Fabric, to create quick access to files.

I'm getting the follow error calling an async function with _allItems

(property) AatResources._allItems: IDocument[] Type 'Promise' is missing the following properties from type 'IDocument[]': length, pop, push, concat, and 16 more.ts(2740)

Any help would be much appreciated

so my code

    export interface IDocument {
      key: string;
      name: string;
      value: string;
      iconName: string;
      webUrl:string;
      fileType: string;
      modifiedBy: string;
      dateModified: string;
      dateModifiedValue: number;
      fileSize: string;
      fileSizeRaw: number;
      contentType:string;
      parentId:string;
      parentDriveId:string;
    } 



     export default class AatResources extends React.Component<IAatResourcesProps, IAatResourcesState> {
      private _allItems: IDocument[];
          constructor(props: IAatResourcesProps) {
              super(props);
              this._allItems = _getDocuments('shared', default_dir_id , default_drive_id, '', '');
          }
      // render, etc...
      }

The async function

MSGraph class is from https://www.techmikael.com/2018/09/example-of-wrapper-to-ease-usage-of.html

  async function _getDocuments(drive_type: any, dir_id: any, drive_id: any, action: any, item_id: any) : Promise<IDocument[]>  {

  const items: IDocument[] = []; 
  let graphUrl :string; 

  await MSGraph.Init(this.context);

  if (drive_type == 'me' && !dir_id) {
    graphUrl = "/me/drive/root/children";
  }

  let response = await MSGraph.Get(graphUrl);

  if (response) {
    var contentType;
    response.value.map((item: any) => {
      if(item.folder){
        contentType = 'folder';
      }else{
        contentType = 'file';
      }
      items.push({
        key:item.id,
        name:item.name,
        value:item.name,
        iconName:'',
        fileType: '',
        webUrl:item.webUrl,
        fileSize: item.size,
        fileSizeRaw: item.size,
        dateModified: item.lastModifiedDateTime,
        modifiedBy: item.lastModifiedBy.user.displayName,
        dateModifiedValue: item.lastModifiedDateTime,
        contentType:contentType,
        parentId:item.parentReference.id,
        parentDriveId:item.parentReference.driveId,
      });
    });

    this.setState({
      items: items
    });

    return items
  }

}

alternatively using a sync function with a callback, I don't get the error but I'm having trouble returning the items array

function _getDocuments(drive_type: any, dir_id: any, drive_id: any, action: any, item_id: any) {

  let graphUrl :string;

  const items: IDocument[] = [];

  if (drive_type == 'me' && !dir_id) {
    graphUrl = "/me/drive/root/children";
  }

    this.props.context.msGraphClientFactory
    .getClient()
    .then((client: MSGraphClient): void => {
      client
        .api(graphUrl)
        .version("v1.0")
        .get((error, response: any, rawResponse?: any) => {

          if (error) {
            console.error(error);
            return;
          }
          if (response) {
            //console.log(response);
            //var driveItems: Array<IDocument> = new Array<IDocument>();
            var contentType;
            response.value.map((item: any) => {
              if(item.folder){
                contentType = 'folder';
              }else{
                contentType = 'file';
              }
              items.push({
                key:item.id,
                name:item.name,
                value:item.name,
                iconName:'',
                fileType: '',
                webUrl:item.webUrl,
                fileSize: item.size,
                fileSizeRaw: item.size,
                dateModified: item.lastModifiedDateTime,
                modifiedBy: item.lastModifiedBy.user.displayName,
                dateModifiedValue: item.lastModifiedDateTime,
                contentType:contentType,
                parentId:item.parentReference.id,
                parentDriveId:item.parentReference.driveId,
              });
            });
            //add callback ?
          }
        });
    });

    this.setState({
      items: items
    });
    return items;
}

What I don't get, if I call the example function from Microsoft (https://developer.microsoft.com/en-us/fabric#/controls/web/detailslist) it doesn't error or say there's a missing promise, it works fine!

function _getDocuments(){

  const items: IDocument[] = [];

  for (let i = 0; i < 500; i++) {
    const randomDate = _randomDate(new Date(2012, 0, 1), new Date());
    const randomFileSize = '0';
    const randomFileType = _randomFileIcon();
    let fileName = 'test1';
    fileName = fileName.charAt(0).toUpperCase() + fileName.slice(1).concat(`.${randomFileType.docType}`);
    let userName = 'un';
    userName = userName
      .split(' ')
      .map((name: string) => name.charAt(0).toUpperCase() + name.slice(1))
      .join(' ');
    items.push({
      key: i.toString(),
      name: fileName,
      value: fileName,
      iconName: randomFileType.url,
      fileType: randomFileType.docType,
      webUrl:'',
      modifiedBy: userName,
      dateModified: randomDate.dateFormatted,
      dateModifiedValue: randomDate.value,
      fileSize: '0',
      fileSizeRaw: 0,
      contentType:'',
      parentId:'',
      parentDriveId:'',
    });
  }
  return items;
}

I've tried removing the parameters and testing it by declaring within the function, but still errors.

Was it helpful?

Solution 2

I resolved this by moving it in to a public modifier

public async _getDocuments(....

I also had to bind 'this' as this.props.context.msGraphClientFactory was undefined using

import autobind from 'react-autobind';

then adding in the class

constructor(props: IProps) {
    super(props);
    autobind(this)
...

OTHER TIPS

In your async version, you are returning the items array (of type IDocument[]), but an async function needs to return a promise. The promise can contain a reference to the populated array.

React will handle it for you, if you specify async, but then also specify the return type to be a typed Promise. It will take your returned array and wrap it in a promise for you. In your case, you want to set return type of _getDocuments to Promise<IDocument[]>, so your declaration would look something like this:

async function _getDocuments(drive_type: any, dir_id: any, drive_id: any, action: any, item_id: any) : Promise<IDocument[]> {

In addition, in order for React to be able to interpret your return state as the resolving of a Promise, you first have to return the Promise, then you can return the results. Give this version a try:

function _getDocuments(drive_type: any, dir_id: any, drive_id: any, action: any, item_id: any) : Promise<IDocument[]> {

  let graphUrl :string;

  const items: IDocument[] = [];

  if (drive_type == 'me' && !dir_id) {
    graphUrl = "/me/drive/root/children";
  }

    return this.props.context.msGraphClientFactory
    .getClient()
    .then((client: MSGraphClient): void => {
      client
        .api(graphUrl)
        .version("v1.0")
        .get((error, response: any, rawResponse?: any) => {

          if (error) {
            console.error(error);
          }

          if (response) {
            //console.log(response);
            //var driveItems: Array<IDocument> = new Array<IDocument>();
            var contentType;
            response.value.map((item: any) => {
              if(item.folder){
                contentType = 'folder';
              }else{
                contentType = 'file';
              }
              items.push({
                key:item.id,
                name:item.name,
                value:item.name,
                iconName:'',
                fileType: '',
                webUrl:item.webUrl,
                fileSize: item.size,
                fileSizeRaw: item.size,
                dateModified: item.lastModifiedDateTime,
                modifiedBy: item.lastModifiedBy.user.displayName,
                dateModifiedValue: item.lastModifiedDateTime,
                contentType:contentType,
                parentId:item.parentReference.id,
                parentDriveId:item.parentReference.driveId,
              });
            });

            this.setState({
               items: items
            });

             return items;
          }
        });
    });
}

Note the first return happens where you initiate the asynchronous call to the Graph API, this allows your function to return quickly and continue performing the rest of the operation as normal. Then, once you have assembled your result set, you can return your items, which fulfills the promise.

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top