SPFx – Using PnP Caching

Introduction

Hi Friends, in this post let us see how we can make use of PnP Caching feature in SPFx solution.

Why do we need to use Caching?

All the application will communicate with the backend store to retrieve the data and then to be displayed to the end users. What if there is a possibility of data that are not meant to be changed often in a day. Even for this type of non-modified data, the communication to the backend store is waste of bandwidth, waste of time etc., though it took you less than a second, if you calculate on a large scale for the entire organization, it is huge. So, in order to reduce this communication, we can use a feature called caching.

What is the use of Caching?

Caching will allow us to store the information in the browser store that can be either local or session. Once the information are stored on the browser, we dont actually need to get the data from the backend store, we can directly pull the data from the browser cache which will also boost the page performance.

PnPCaching?

@pnp/odata has a built-in module for caching the items that are pulled from SharePoint. Its a helper module which will take care of the boiler plate code to write and read the cache. This method by default check the cache store and returns if the data is available and not expired, if the data is not available and if it is expired, then it pulls the data from SharePoint, update the cache and returned to the end users. It can be used by calling usingCaching() with the pnp method. The usingCaching() can also be combined with batch process.

Note: When you are using the method usingCaching(), it should always be the last method before the get(). Also, caching will work only for the get request type. If you are using the default caching configuration, the data is stored in the session storage by default and it will expire after 1 minute.

Focus on the Code

Let us start by creating a new web part project using yeoman SharePoint generator, before that create a folder where you want to create the web part. Navigate to that folder and run the below command.

yo @microsoft/sharepoint

The generator will asks you couple of questions,

  • Enter the webpart name as your solution name, and then select Enter.
  • Select Create a subfolder with solution name for where to place the files.
  • Select Y to allow the solution to be deployed to all sites immediately.
  • Select N on the question if solution contains unique permissions.
  • Enter the webpart name
  • Enter the webpart description
  • Choose the framework as ‘React

Once the project is created, install the required pnp modules from npm using the below command. I had created a webpart with a name PnPCaching.

npm i @pnp/sp @pnp/odata @pnp/common --save-exact

Open the code in VSCode which is my favorite and flexible code editor for SharePoint Framework. You can directly open the project folder from the file menu or use the below command to open the VSCode from command line.

cd \web part folder\
code .

Navigate to the webpart.ts file and copy-paste the below code.

Import the SP reference from the PnP module.

import { sp } from '@pnp/sp';

Setup the SP object with the current context and also the global configuration for the caching. Copy-paste the below code under the default class.

public onInit(): Promise<void> {        
        return super.onInit().then(_ => {
            sp.setup({
                spfxContext: this.context,
                defaultCachingStore: "local", //"session"
                defaultCachingTimeoutSeconds: 30,
                globalCacheDisable: false // or true to disable caching in case of debugging/testing
            });
        });
    }

The above code is very simple, we are the doing the following

  1. Passing the current context to the spfxContext param.
  2. Setting the default cache store to be local.
  3. Setting the default cache timeout seconds to 30, so that the cached value will expire after 30 seconds.
  4. Setting up the disable cache to false for production package and it can be set to true for development and testing purpose.

Note: PnPCaching can be used with the global configuration as well as we can configure the params for individual call based on our requirement.

Navigate to your webpart.tsx file and copy-paste the below code.

import * as React from 'react';
import styles from './PnPCaching.module.scss';
import { IPnPCachingProps } from './IPnPCachingProps';
import { DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { sp } from '@pnp/sp';
import "@pnp/sp/webs";
import "@pnp/sp/lists/web";
import "@pnp/sp/items/list";
import { dateAdd } from '@pnp/common';

export interface IPnPCachingState {
    loading: boolean;
    result: any;
}

export default class PnPCaching extends React.Component<IPnPCachingProps, IPnPCachingState> {

    constructor(props: IPnPCachingProps) {
        super(props);
        this.state = {
            loading: false,
            result: null
        };
    }

    public _usingGlobalConfig = async () => {
        this.setState({ loading: true, result: null });
        let depts = await sp.web.lists.getByTitle('Department').items.usingCaching().get();
        this.setState({ loading: false, result: JSON.stringify(depts, null, 4) });
    }

    public _usingPerCallCache = async () => {
        this.setState({ loading: true, result: null });
        let regions = await sp.web.lists.getByTitle("Region").items.usingCaching({
            key: 'PnP_Region',
            expiration: dateAdd(new Date(), 'minute', 5),
            storeName: 'local'
        }).get();
        this.setState({ loading: false, result: JSON.stringify(regions, null, 4) });
    }

    public render(): React.ReactElement<IPnPCachingProps> {
        return (
            <div className={styles.pnPCaching}>
                <div className={styles.container}>
                    <div className={styles.row}>
                        <div className={styles.column}>
                            <DefaultButton onClick={this._usingGlobalConfig}>Get Departments (using global cache)</DefaultButton>
                            <DefaultButton onClick={this._usingPerCallCache}>Get Regions (using per call cache)</DefaultButton>
                            {this.state.loading &&
                                <div><h4>Please wait, loading...</h4></div>
                            }
                            {this.state.result &&
                                <div style={{ wordBreak: 'break-word', maxHeight: '400px', overflowY: 'auto' }}>
                                    <pre>{this.state.result}</pre>
                                </div>
                            }
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

Let us see the code in-detail.

  1. The state interface is declared only for showing the loading and results for understanding. This may change based on your requirement.
  2. We have declared 2 methods
    • _usingGlobalConfig – This method pulls the list of departments from the ‘Department‘ list and store it in the cache using the global configuration.
    • _usingPerCallCache – This method pulls the list of regions from the ‘Region‘ list and store it in the cache using the configuration defined in the call.
  3. In the render method, we have declared 2 buttons to call the above methods and then to display the results.

Preview

Once the solution is ran, you can check your browser console to check whether the information is stored or not.

Source Code

The source code along with other samples can be found in the below github link.

SPFx-Demos

Happy Coding…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s