Building a Basic SPFx Web Part with PnP.js and React Hooks

This article will guide you through the process of creating a SharePoint Framework (SPFx) web part that retrieves items from a SharePoint list using PnP.js and React Hooks. We will cover the necessary components, configurations, and how to manage state and side effects using React Hooks.

1. Prerequisites

Before starting, ensure that you have the following installed:

  • Node.js (version 10 or later)
  • Yeoman and SharePoint Framework Yeoman generator
  • Basic understanding of TypeScript and React

2. Creating the SPFx Web Part

Use the following command to create a new SPFx web part:

yo @microsoft/sharepoint

Follow the prompts to set up your project, selecting options that fit your needs. Ensure that you include React as your framework.

3. Setting Up PnP.js

To use PnP.js, you need to install it in your project. Run the following command in your project directory:

npm install @pnp/sp @pnp/graph @pnp/logging

3.1. Configuring PnP.js

Create a new file named pnpjsConfig.ts in the src/biblioteca folder:

/* eslint-disable no-var */
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { ISPFXContext, spfi, SPFI, SPFx as spSPFx } from "@pnp/sp";
import { graphfi, GraphFI, SPFx as graphSPFx } from "@pnp/graph";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
import "@pnp/sp/batching";

var _sp: SPFI;
var _graph: GraphFI;

export const getSP = (context?: WebPartContext): SPFI => {
  if (_sp === undefined || _sp === null) {
    _sp = spfi().using(spSPFx(context as ISPFXContext));
  }
  return _sp;
};

export const getGraph = (context?: WebPartContext): GraphFI => {
  if (_graph === undefined || _graph === null) {
    _graph = graphfi().using(graphSPFx(context as ISPFXContext));
  }
  return _graph;
};

4. Creating the Web Part

Now, create the main web part file named PnPGetItensBasicoReactHooksWebPart.ts:

import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import { type IPropertyPaneConfiguration, PropertyPaneTextField } from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { getSP } from '../biblioteca/pnpjsConfig';

import * as strings from 'PnPGetItensBasicoReactHooksWebPartStrings';
import PnPGetItensBasicoReactHooks from './components/PnPGetItensBasicoReactHooks';
import { IPnPGetItensBasicoReactHooksProps } from './components/IPnPGetItensBasicoReactHooksProps';

export interface IPnPGetItensBasicoReactHooksWebPartProps {
  description: string;
}

export default class PnPGetItensBasicoReactHooksWebPart extends BaseClientSideWebPart<IPnPGetItensBasicoReactHooksWebPartProps> {

  public sp:any;

  public render(): void {
    const element: React.ReactElement<IPnPGetItensBasicoReactHooksProps> = React.createElement(
      PnPGetItensBasicoReactHooks,
      {
        sp: this.sp,
      }
    );

    ReactDom.render(element, this.domElement);
  }

  protected async onInit(): Promise<void> {
    this.sp = getSP(this.context);
  }

  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
                })
              ]
            }
          ]
        }
      ]
    };
  }
}

5. Creating the React Component

Next, create a file named PnPGetItensBasicoReactHooks.tsx for your React component:

import * as React from 'react';
import { useState, useEffect } from 'react';
import { IPnPGetItensBasicoReactHooksProps } from './IPnPGetItensBasicoReactHooksProps';

const PnPGetItensBasicoReactHooks: React.FC<IPnPGetItensBasicoReactHooksProps> = ({ sp }) => {
  const [items, setItems] = useState<any[]>([]);
  const [loading, setLoading] = useState<boolean>(true);

  // This hook executes the data fetching only once, after the first render.
  useEffect(() => {
    const fetchData = async () => {
      try {
        const listItems = await sp.web.lists.getByTitle("ListaExemplo").items();
        setItems(listItems); // Set items in state
      } catch (error) {
        console.error("Error fetching list items:", error);
      } finally {
        setLoading(false); // Disable loading state
      }
    };

    fetchData();
  }, [sp]); // The hook depends on `sp`

  if (loading) {
    return <div>Loading...</div>; // Display loading while items are not loaded
  }

  return (
    <div>
      <h2>List Items - Hooks</h2>
      <ul>
        {items.map(item => (
          <li key={item.Id}>{item.Title}</li>
        ))}
      </ul>
    </div>
  );
}

export default PnPGetItensBasicoReactHooks;

6. Understanding React Hooks

6.1. The useState Hook

The useState hook allows you to add state to a functional component. The basic syntax is:

const [state, setState] = useState(initialState);
  • Initial State: In our case, items is initialized as an empty array. This means we start without any items loaded. As we fetch data from the SharePoint list, we will call setItems to update the state with the fetched items.

6.2. The useEffect Hook

The useEffect hook allows you to perform side effects in functional components. Side effects are operations that can affect other components and cannot be performed during rendering. Examples include API calls, directly manipulating the DOM, and setting timers.

The basic syntax of useEffect is:

useEffect(() => {
  // Effect code
}, [dependencies]);
  • First Argument: A function containing the effect code.
  • Second Argument: An array of dependencies. The effect runs after the first render and whenever one of the dependencies changes.

In our example, we have:

useEffect(() => {
  const fetchData = async () => {
    // Fetch logic
  };

  fetchData();
}, [sp]);

Explanation of the Code

Inside useEffect, we define an asynchronous function called fetchData, responsible for fetching items from the SharePoint list.

  • Single Execution: The dependency array contains sp, our PnP.js object. This means the effect will execute once after the first render and whenever the sp value changes.
const fetchData = async () => {
  try {
    const listItems = await sp.web.lists.getByTitle("ListaExemplo").items();
    setItems(listItems); // Update state with items
  } catch (error) {
    console.error("Error fetching list items:", error);
  } finally {
    setLoading(false); // Disable loading state
  }
};

Within fetchData, we call the SharePoint API using sp.web.lists.getByTitle("ListaExemplo").items(). When items are returned, we call setItems(listItems) to update the items state. This causes the component to re-render with the newly available data.

  • Error Handling: If an error occurs, it is caught and logged to the console for debugging.
  • Updating Loading State: Regardless of whether the fetch operation succeeds or fails, setLoading(false) is called in the finally block, indicating that the fetch operation has completed.

6.3. Summary of Hooks

The useState and useEffect hooks are powerful tools that make managing state and side effects in React components simpler and more intuitive. With useState, you can add state to functional components, while useEffect allows you to manage side effects efficiently, ensuring your UI stays in sync with your data.

These hooks are especially useful in applications that need to interact with APIs and manage data in real time, like our example that fetches and displays items from a SharePoint list.

7. Conclusion

In this article, we created a basic SPFx web part

that retrieves items from a SharePoint list using PnP.js and React Hooks. We learned how to use the useState and useEffect hooks to manage state and side effects effectively in a React functional component. This setup provides a foundation for building more complex SPFx web parts.

As you continue to work with SPFx, PnP.js, and React, you’ll discover more advanced patterns and techniques to enhance your applications. Experiment with different SharePoint APIs and React features to deepen your understanding and expand your capabilities as a developer.


Summary of Commands

Here’s a quick summary of the commands and code snippets used in this guide:

  • Create SPFx Project:
  yo @microsoft/sharepoint
  • Install PnP.js:
  npm install @pnp/sp @pnp/graph @pnp/logging
  • Render Component:
  const element: React.ReactElement<IPnPGetItensBasicoReactHooksProps> = React.createElement(
    PnPGetItensBasicoReactHooks,
    { sp: this.sp }
  );
  • Fetch Data:
  const listItems = await sp.web.lists.getByTitle("ListaExemplo").items();

Edvaldo Guimrães Filho Avatar

Published by

Categories:

Leave a comment