1

My Approach is

  1. default 6 items, using api query of the $top=6
  2. If a button is pressed, clear screen, get all data using qpi query without $top6

Algorithm:

  • Define a function to loadNews(boolean=false) This defaults to get 6 items only, API query with $top=6
  • Create a button and onClick event pass true to the function
  • If true is passed to the loadNews();
  • this.forceUpdate();
  • clear the contents of the data array
  • fetch all data using another API query without $top=6

What will be another better approach that can boost performance? Is it possible to add on show additional items without having to load all data again?

This is my code.

import * as React from 'react';
import styles from './A.module.scss';
import { IAProps } from './IAProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { PageContext } from "@microsoft/sp-page-context";
import { HttpClient, IHttpClientOptions, HttpClientResponse, SPHttpClient, ISPHttpClientOptions, SPHttpClientResponse } from '@microsoft/sp-http';
import {
  Environment,
  EnvironmentType
} from '@microsoft/sp-core-library';
import {Button} from 'office-ui-fabric-react/lib/Button';

export interface ISPLists {
  value: ISPList[];
};

export interface Istate {
  ID:string;
  Title: string;
  isLoad?: boolean;
  Url:string;
    AttachmentFiles:{
      Name:string;
      Url:string;
    ServerRelativeUrl: string;

    };
}


export interface ISPList {
  ID:string;
  Title: string;
  Summary : string;
  NewsCategory: string;
  Created:string;
    AttachmentFiles:{
      Name:string,
      Url:string,
    ServerRelativeUrl: string,
    };

}






/*
MAIN STARTS HERE
2020-01-28
*/
export default class A extends React.Component<IAProps> {

constructor(props){
  super(props);

   this.loadNews();

   this.getListData=this.getListData.bind(this);
   this.geturl=this.geturl.bind(this);
   this.loadNews=this.loadNews.bind(this);
}



//variables to render html tags
private data:any =[];





private loadNews(showall=false){
  if(showall){
    this.data.length = 0;
    this.forceUpdate();
    this.getListData(showall);
  }else{
    this.getListData(showall); // show 6 item by default
  }

}



  public geturl(query:string){
    var path="/Lists/News/DispForm.aspx?ID=";
    var currdir=this.props.pagecontext.web.absoluteUrl;
   var result=currdir+path+query;
       return result;
   }



   private  async getListData(ShowAll) {
     var qurl;
      if (ShowAll)
     {
       qurl="/_api/web/lists/getbytitle('News')/items?$select=*,AttachmentFiles&$expand=AttachmentFiles/Title&$orderby=ID desc";
     }else{
       qurl="/_api/web/lists/getbytitle('News')/items?$select=*,AttachmentFiles&$expand=AttachmentFiles/Title&$orderby=ID desc&$top=6";
     }
    const opt: ISPHttpClientOptions = { headers: { 'Content-Type': 'application/json;odata=verbose' } };
    this.props.SPHttpClient.get(this.props.pagecontext.web.absoluteUrl + qurl, SPHttpClient.configurations.v1, opt).then((response: SPHttpClientResponse) => {
      response.json().then((json: any) => {

         for(let i=0;i<json.value.length;i++){
          var url=this.geturl(json.value[i].ID);

            this.data.push({
              ID:json.value[i].ID,
             Title:<a href={url} target="_blank">{json.value[i].Title}</a>,
             Desc:json.value[i].Description,
             More:url,
             summ:json.value[i].Summary,
             Url:<img className={styles.image} src={json.value[i].AttachmentFiles[0] ? json.value[i].AttachmentFiles[0].ServerRelativeUrl: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQy7LYu4CcprObfi6O9Vt7TMhFfT0jKju7koQOdUKH3_ZXmqq2G&s'} />
              });  




             //   debugger;
               this.forceUpdate();
              }//close for
        });// close response.json
      });//close private getListData method

    }



  public render(): React.ReactElement<IAProps>{

       return (



        <div className={styles.thumbnails}>
        <div className={styles.title}>
          最新消息
            </div>

        {this.data.map((data)=>

          <div className={styles.box}>

                          {data.Url}

                          <div className={styles.p}>
                            {data.Title}
                          </div>
                              <div className={styles.h3}>
                                  {data.summ}
                              </div>
                                   <div className={styles.more}>
                                       <a href={data.More} target="_blank">More...</a>
                                   </div>

           </div>
        )}

        <div className={styles.showall}>
          <button onClick={() => this.loadNews(true)}>More </button>
        </div>
         </div>










    );
  }
}



2 Answers 2

3

While willman's critique of your code is correct (I was going to add all that into my answer as well - you should definitely be saving that data in state, not a class property, and relying on the fact that when you call setState(), React will automatically re-render, without having to call forceUpdate()), I want to answer your real question:

What will be another better approach that can boost performance? Is it possible to add on show additional items without having to load all data again?

I would say you should only really be concerned with the "performance" of making API calls if you know you are going to have users dealing with extremely slow network connections. The way I see it, you have a few options to do what you want: initially show only 6 items, and only show more if the users actively want to see more.

  1. You could just automatically request all items with your initial API call, and not use $top=6, but when you get the data, only show the first 6 in the UI. That way, if the users request to see more, you already have it all in memory and don't need to make another API call, you just show more of what you already have.
  2. Do what you're planning to do - get the first 6 using $top=6, and if the users ask for more, just reload without $top=6.
  3. I'm not sure exactly how it's exposed in the response when making requests through the SPHttpClient in SPFx, but when making straight REST API calls, if you use $top, there will be a __next property on the response object that has a URL for you to get the next items, the same number that you specified in your $top. So in your case, if you did $top=6, your first API call would get the first 6 items, then in the response would also be a __next property with a URL you could use to get the next 6 items.

In any case, it really boils down to two approaches:

  • Make one API call and load everything, and deal with what gets shown in your code, or
  • Make multiple API calls

(My other suggestion to you, not related to API calls but related to React coding patters, would be to not start off your first API call from within the constructor. You should do it from componentDidMount. Here's some info on the React lifecycle methods.)

1

You should not be storing your results in a class variable and calling forceUpdate. Instead, you should store the results in state (see docs on state). React is purposefully built to run your render method whenever state changes. That way you don't have to blank anything out, when they click the button, the first 6 items stay there until your api call finished then is instantly replaced by the full dataset.

So, if you change add resultData: Array to your IState, and change your class definition to include IState, extends React.Component<IAProps, IState>, then change your api callback to something like this:

this.props.SPHttpClient.get(this.props.pagecontext.web.absoluteUrl + qurl, SPHttpClient.configurations.v1, opt).then((response: SPHttpClientResponse) => {
      response.json().then((json: any) => {
         let tempResults = [];

         for(let i=0;i<json.value.length;i++){
          var url=this.geturl(json.value[i].ID);

            tempResults.push({
              ID:json.value[i].ID,
             Title:<a href={url} target="_blank">{json.value[i].Title}</a>,
             Desc:json.value[i].Description,
             More:url,
             summ:json.value[i].Summary,
             Url:<img className={styles.image} src={json.value[i].AttachmentFiles[0] ? json.value[i].AttachmentFiles[0].ServerRelativeUrl: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQy7LYu4CcprObfi6O9Vt7TMhFfT0jKju7koQOdUKH3_ZXmqq2G&s'} />
              });  

               this.setState( { resultData: tempResults });
              }//close for
        });// close response.json

Then finally change your render method to use this.state.resultData.map instead of this.data.map.

Now, with all of that said, once that is complete, you may want to implement paging so that the user can choose to get records in chunks or pages of ten or twenty at a time, instead of just choosing between 6 records or all.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.