Showing posts with label SharePoint Framework (SPFx). Show all posts
Showing posts with label SharePoint Framework (SPFx). Show all posts

June 5, 2025

React Hooks vs Class Components: A Beginner-Friendly Comparison

If you’re new to React or have experience with class components, you may have heard that hooks have brought significant changes to the way React applications are developed.

Early in React’s history, class components were the standard approach., class components were everywhere. Managing state, dealing with lifecycle methods like componentDidMount, and passing data using context often felt confusing and repetitive. With the introduction of hooks, React development became simpler and cleaner.

In this blog, I want to walk you through the basics of four important hooks:

  • useState
  • useEffect
  • useRef
  • useContext

For each hook, I’ll show you what code looked like before hooks (using classes) and how much easier it is with hooks. I’ll also explain the pain points and benefits from my perspective.

1. useState: Managing Local Component State

Let’s start with the most used hook - useState.

Without Hooks (Class Component)

class Counter extends React.Component {
    state = { count: 0 };
 
    increment = () => {
      this.setState({ count: this.state.count + 1 });
    };
 
    render() {
      return (
        <div>
          <h3>Count: {this.state.count}</h3>
          <button onClick={this.increment}>Increment</button>
        </div>
      );
    }
  }

With Hook: useState (Function Component)

import React, { useState } from "react";
function Counter() {
    const [count, setCount] = useState(0);
 
    return (
      <div>
        <h3>Count: {count}</h3>
        <button onClick={() => setCount(count + 1)}>Increment</button>
      </div>
    );
  }

What Was Difficult Without Hooks:
  • You had to use this.state, this.setState, and bind functions.
  • Even for small components, you needed a class.
  • State logic wasn’t reusable.

Why useState is Better:
  • Much cleaner and easier to read.
  • No more dealing with this.
  • You can manage multiple states independently in one component.

2. useEffect: Doing Side Effects (e.g. API Calls, Timers)

When your component needs to fetch data, set up a timer, or do something after render - you use useEffect.

Without Hooks (Class Component)

class Timer extends React.Component {
  state = { seconds: 0 };

  componentDidMount() {
    this.interval = setInterval(() => {
      this.setState(prev => ({ seconds: prev.seconds + 1 }));
    }, 1000);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }

  render() {
    return <h3>Time: {this.state.seconds} seconds</h3>;
  }
}

With Hook: useEffect(Function Component)

import React, { useEffect} from "react";
function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => setSeconds(s => s + 1), 1000);
    return () => clearInterval(interval);
  }, []);

  return <h3>Time: {seconds} seconds</h3>;
}


What Was Difficult Without Hooks:
  • You had to spread your logic across multiple lifecycle methods.
  • It was hard to keep related logic together.
  • Cleanup code (like stopping a timer) was messy.

Why useState is Better:
  • Everything lives in one place.
  • Cleanup is easy with the return function.
  • Execution is controlled by specifying dependencies in the array.


3. useRef: Getting a Reference to a DOM Element or Persistent Value

If you’ve ever needed to directly access an input field or persist a value between renders without triggering a re-render — useRef is the way to go.

Without Hooks (Class Component)

class FocusInput extends React.Component {
  constructor() {
    super();
    this.inputRef = React.createRef();
  }

  focus = () => {
    this.inputRef.current.focus();
  };

  render() {
    return (
      <div>
        <input ref={this.inputRef} type="text" />
        <button onClick={this.focus}>Focus Input</button>
      </div>
    );
  }
}

With Hook: useRef (Function Component)

import React, { useRef} from "react";
function FocusInput() {
    const inputRef = useRef(null);
 
    return (
      <div>
        <input ref={inputRef} type="text" />
        <button onClick={() => inputRef.current.focus()}>Focus Input</button>
      </div>
    );
  }

What Was Hard Without Hooks:

  • You needed constructors and had to manage refs manually.
  • It made the component more complex.

Why useRef is Better:

  • You can declare and use it easily in functional components.
  • It’s perfect for keeping mutable values around without causing re-renders.

4. useContext: Accessing Global Data Easily

When you want to pass down data (like theme or user info) to deeply nested components, useContext is your friend.

Without Hooks (Class Component)

const ThemeContext = React.createContext('light');

class ThemedButton extends React.Component {
  static contextType = ThemeContext;

  render() {
    const theme = this.context;
    return <button style={{ background: theme === 'dark' ? '#333' : '#eee' }}>
             Theme: {theme}
           </button>;
  }
}

With Hook: useContext (Function Component)

import React, { useContext} from "react";
const ThemeContext = React.createContext('light');

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button style={{ background: theme === 'dark' ? '#333' : '#eee' }}>
           Theme: {theme}
         </button>;
}

What Was Hard Without Hooks:

  • You had to use static contextType.
  • It wasn’t easy to use multiple contexts at once.
  • More boilerplate.

Why useContext is Better:

  • Just call useContext in any functional component.
  • Clean and simple syntax.
  • Great for global state like auth, theme, user, etc.

Final Thoughts

Hooks made React fun again. No more fighting with this, long lifecycle methods, or confusing state logic.

For those new to hooks, it is recommended to start by gradually converting class components to functional components using hooks. As familiarity with hooks increases, many developers find them to be a more efficient and maintainable approach to building React applications.

May 1, 2025

Fixing SPFx Build Error: “UglifyJs Unexpected Token” When Running gulp bundle --ship

Introduction:

While packaging your SharePoint Framework (SPFx) solution using the production command: 

gulp bundle --ship

You may encounter a frustrating error message like: 

Unexpected token: name (corefeature) - SPFx while build solution

This usually indicates that UglifyJS, the default minifier in SPFx, stumbled upon ES6+ syntax (e.g., class, let, const) that it does not understand. 

In this post, I will guide you through a clean and effective workaround using terser-webpack-plugin, a modern minifier that fully supports ES6+. 

Why This Error Occurs:

  • Root Cause: UglifyJS does not support modern JavaScript (ES6+). 
  • Impact: Webpack fails during the minification process, stopping the bundle process for production. 
  • Trigger: Usage of ES6+ syntax like class, const, etc., in your SPFx web part code.  

Solution: Swap UglifyJS with Terser:

To resolve this, we will: 

  1. Add Terser and Webpack merge dependencies. 
  2. Update gulpfile.js to override the default SPFx Webpack configuration. 
  3. Clean and rebuild your project. 

Step-by-Step Fix:

Step 1: Install Compatible Dependencies:

Update your package.json to include: 

"terser-webpack-plugin-legacy": "1.2.3",
"webpack-merge": "4.2.1"

Then run the following commands in your terminal: 

npm install terser-webpack-plugin --save-dev
npm install terser-webpack-plugin-legacy --save-dev
npm install webpack-merge@4.2.1 --save-dev

Optional (if Babel is needed for ES6+ transpilation): 

npm install @babel/core @babel/preset-env babel-loader --save-dev

Step 2: Update gulpfile.js:

Modify your gulpfile.js as shown below: 

'use strict';
 
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
const merge = require('webpack-merge');
const TerserPlugin = require('terser-webpack-plugin-legacy');
 
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase
  and will not be type-safe.`);

build.initialize(gulp);
 
build.configureWebpack.setConfig({
 additionalConfiguration: function (config) {
   config.plugins = config.plugins.filter(plugin => !(plugin.options && plugin.options.mangle));
 
   return merge(config, {
     optimization: {
       minimize: true,
       minimizer: [new TerserPlugin()]
     }
   });
 }
});

This code replaces UglifyJS with Terser during the bundle phase. 

Step 3: Clean & Rebuild the Project: 

Run the following commands in sequence: 

gulp clean
gulp build --ship
gulp bundle --ship

Your project should now build successfully, free of any “unexpected token” errors from UglifyJS. 

Optional: Babel Setup (Only If Needed): 

If your project uses newer JavaScript features not supported in your target environments, consider setting up Babel. However, for the UglifyJS error alone, swapping in Terser is typically enough. 

Conclusion: 

  • If you are getting the "UglifyJs Unexpected Token" error while bundling your SPFx project, it is because the default minifier does not support modern JavaScript. By switching to it terser-webpack-plugin, you can fix the issue and bundle your project without errors. Just follow the steps to update your packages and gulpfile, and you will be good to go!
  • If you run into any issues while implementing this solution, feel free to drop a comment. I will be happy to help. 

If you have any questions you can reach out our SharePoint Consulting team here.

April 27, 2023

How to trigger Power Automate from SPFx webpart and return result back to SPFx webpart?

Introduction:

In this blog, we will learn how we can trigger Power Automate from SPFx web part, fetch some data or perform any operation and return result back to SPFx web part.

Scenario:

We need to fetch data from API which was not accessible by SharePoint site due to CORS issue. So we created Power Automate which will triggered from SPFx web part, and this flow will fetch the data from API and return the result to SPFx web part.

Implementation:

Follow below steps to trigger Power Automate from SPFx web part and return result back to SPFx web part.

Step 1: First, we need to create Power Automate which will have trigger of When HTTP request is received.

Note: HTTP POST URL will be generated only when you Save the Power Automate.

Step 2: If we want to pass some data from SPFX web part to Power Automate, we need to specify JSON schema in Request Body JSON Schema. 
For example, we can use JSON Schema for Startdate and Enddate as below:
{
    "type": "object",
    "properties": {
        "Startdate": {
            "type": "string"
        },
        "Enddate": {
            "type": "string"
        }
    }
}

Step 3: Now, in Power Automate we need to action for Response at the end of the Power Automate which will return the response to SPFX web part.

Step 4: Now, you can save the flow and copy the HTTP POST URL from first action, which we will use this URL in SPFX web part.

Step 5: In a SPFX web part, first we need to import below namespaces.
import { HttpClient, HttpClientResponse, IHttpClientOptions} from '@microsoft/sp-http';

Step 6: Now, we will create a new method which will be called when we want to trigger Power Automate. So in that method, first we will create a body, which will pass data to Power Automate:

const body: string = JSON.stringify({
      'Startdate': '1-1-2023',
      'Enddate': 12-31-2023
 });

Step 7: Now, we will create header, which will be used when we trigger Power Automate:
const requestHeaders: Headers = new Headers();
requestHeaders.append('Content-type', 'application/json');

Step 8: As we will be using HTTP request, we need to use body and headers in HTTPClient Options:

const httpClientOptions: IHttpClientOptions = {
      body: body,
      headers: requestHeaders
};

Step 9: Now we will trigger the flow using HTTP request as shown in below code:

 return this.props.context.httpClient.post(
      postURL, //This will be the URL which is generated in Power Automate
      HttpClient.configurations.v1,
      httpClientOptions)
      .then((response: HttpClientResponse): Promise<HttpClientResponse> => {
        return response.json();
  });
Note: Before we write above code, we need to create new property for context and we need to assign below value:
context: this.context

Step 10: Here is the full code snippet of the method which will trigger Power Automate:
private getMetricsData(StartDate: string, EndDate: string): Promise<HttpClientResponse> {
    const postURL = ""; // This will be the URL which is generated from Power Automate

    const body: string = JSON.stringify({
      'Startdate': StartDate,
      'Enddate': EndDate
    });

    const requestHeaders: Headers = new Headers();
    requestHeaders.append('Content-type', 'application/json');

    const httpClientOptions: IHttpClientOptions = {
      body: body,
      headers: requestHeaders
    };

    return this.props.context.httpClient.post(
      postURL,
      HttpClient.configurations.v1,
      httpClientOptions)
      .then((response: HttpClientResponse): Promise<HttpClientResponse> => {
        return response.json();
      });
  }

Step 11: Now when we call the method, it will trigger the Power Automate and return the result to SPFX web part. 

Conclusion:

This is how we can trigger Power Automate from SPFX web part and return the result back to SPFX web part. Hope this blog will be helpful for you!

If you have any questions you can reach out our SharePoint Consulting team here.

July 7, 2022

How to use Fluent UI Icons in SharePoint Framework (SPFx)?

Introduction:

In this blog, we will learn how we can use Fluent UI Icons in SPFx Webparts or SPFx Extensions. Here are the steps to use Fluent UI icons:

Steps:

Step 1) To use the Fluent UI icons, first we need to install the package for Fluent UI using the below command. 
 npm i @fluentui/react  

Step 2) Now, we need to import Icons and Iconsprops as given below:
 import { Icon } from '@fluentui/react/lib/Icon';  

Step 3) Now, declare the global variable and assign the icon as given in the below code. Here we are using a global variable so we can use this icon multiple times.

 const HomeIcon = () => <Icon iconName="Home" />;  

Step 4) Now, in the render method we can render the icon as given below:
 <HomeIcon></HomeIcon>  

Here is the full code snippet:
 import * as React from 'react';  
 import { IAlertsProps } from './IAlertsProps';  
 import styles from './Alerts.module.scss';  
 import { escape } from '@microsoft/sp-lodash-subset';  
 import { Icon } from "@fluentui/react";  
 const HomeIcon = () => <Icon iconName="Home" />;  
 export default class Alerts extends React.Component<IAlertsProps, {}> {  
  public render(): React.ReactElement<IAlertsProps> {  
   return (  
    <div className={styles.Alerts}>  
     <HomeIcon></HomeIcon>      
    </div>  
   );  
  }  
 }  

It will display the Home Icon in your web part.

Conclusion:

This is how we can use Fluent UI Icons in SPFx Webparts or Extension. Hope this helps!

If you have any questions you can reach out our SharePoint Consulting team here.

June 23, 2022

SharePoint Framework (SPFx) - How to add Request Header while using PnP for SharePoint List CRUD Operations?

Introduction:

In this blog we will learn how can we add the Request Header while using PnP in SharePoint Framework (SPFx) web parts.

Problem Statement:

When we use the Fetch or Ajax requests in SPFx, we have option to add the Request Header to the REST API requests we are using, but when using PnP, there is no option to add the Request Header while performing CRUD operations in SharePoint.

For example, while using Fetch Request to get the list items, we can add the header as give in below code snippet:
 return fetch(APIUrl, {  
    credentials: 'same-origin',  
    headers: {   
         'Accept': 'application/json;odata=verbose',  
         'odata-version': ''   
    }  
   })  
    .then((res) => res.json())  
    .then((result) => {      
     return result.d.results;  
    },  
     (error) => {  
      console.error('Error:', error);  
     }  
    );  

But, to get the list items using PnP, we will use below code and we will not have option to add the headers:
 await web.lists.getByTitle('List Title').items.select("Title").then((response) => {  
    this.setState({ SiteTimeZoneOnly: response[0].TimeZone });  
 });  

Resolution:

How to add headers:

Step 1) Go to the .ts file of your web part.

Step 2) Now, go to the onInit method.

Step 3) Now, we can add the header in onInit method as given in below code snippet:
 protected onInit(): Promise<void> {  
   return super.onInit().then(_ => {  
    // other init code may be present  
    sp.setup({  
     spfxContext: this.context,  
     sp: {  
      headers: {  
        "X-ClientTag": "********"  
      }  
     }  
    });  
   });  
  }  

Here we have added 'X-ClientTag' header. You can also add comma separated multiple headers as shown in the below code snippet.
 sp.setup({  
     spfxContext: this.context,  
     sp: {  
      headers: {  
        "X-ClientTag": "********",  
        "Accept": "application/json;odata=verbose"  
      }  
     }  
    });  

Now your header is added successfully. You can verify the headers from Network tab of browser developer tool as shown in the below screenshot:

Conclusion:

This is how we can add Request Header in SPFx while using PnP to perform SharePoint list/library operations.

If you have any questions you can reach out our SharePoint Consulting team here.

April 21, 2022

How to use Multi-Value Taxonomy Picker Control in the SPFx webpart?

Introduction

Recently we implemented an SPFx web part solution for a Postal and Parcel company based out of Melbourne, Victoria, Australia. We came across a requirement to provide a taxonomy picker control to select the terms for the term set and to store it in the SharePoint list/library. 


There are two ways to store the taxonomy picker's selected value in the SharePoint List/Library based on whether it is a single value or multi-value taxonomy picker. 


We have already shared the details on how to use the single value taxonomy picker control in our previous blog. It can be accessed here.


In this blog, we will learn about multi-value taxonomy picker control and how to use it in the SPFx web part and store its selected term value with other properties/fields in the SharePoint list/library.

Pre-Requisites

  • We need to have a multi-value selectable managed metadata column associated with a term set in the SharePoint list. 
  • We need to have the @pnp/spfx-controls-react package installed in our SPFx web part. We can use the command below to install the package. 
 npm install @pnp/spfx-controls-react --save --save-exact  

  • Create the context and siteUrl props in the SPFx solution. 
  • Create a state of Array type to store the selected taxonomy picker’s value. 

Implementation

    Below are the steps to implement the Taxonomy Picker Control for the multi-value managed metadata column. 

    Step 1: 

    Import the taxonomy picker and IPickerTerms in the .tsx(file that contains the render method) file of the SPFx web part as shown below. 

     import { TaxonomyPicker, IPickerTerms } from "@pnp/spfx-controls-react/lib/TaxonomyPicker";   
    

    Step 2: 

    Implement the Taxonomy Picker Control in the render method as shown below. 
    <TaxonomyPicker
         allowMultipleSelections={true}
         termsetNameOrID="Job Title"
         panelTitle="Select Job Title"
         label=""
         context={this.props.context}
         onChange={this.onJobtitleTaxPickerChange.bind(this)}
         isTermSetSelectable={false}
         initialValues={this.state.setJobTitletaxonomy}
         />
    


    Below are the attributes used in the Taxonomy Picker Control.

    • allowMultipleSelections: This attribute is used for multi-value selection. Please provide value as "true" in order to work with the multi-value taxonomy picker control.
    • panelTitle: We need to provide the Term Set Picker Panel title.
    • Label: We need to provide the text displayed above the Taxonomy Picker. Context: We need to provide the content of the current web part. For example, this.props.context.
    • TermsetNameOrID: We need to provide the term set's name or its GUID.
    • OnChange: This attribute is used to store the selected term in a state so we can use that state's value while submitting The item to the SharePoint list/library. It's an optional attribute. 
    • InitialValues: This attribute is used to set the taxonomy picker's selected values. It's an optional attribute. 

    Step 3: 

    After creating taxonomy control, we need to define a method to handle its on-change event and store the selected term in a state as shown below. 

      private onJobtitleTaxPickerChange(terms: IPickerTerms) {
        this.setState({
          setJobTitletaxonomy: terms
        });
      } 
    

    Step 4: 

    The managed metadata column has a single line of text hidden column associated with it, it's a hidden field and we will use this field with its internal name for the further process. We can retrieve the internal name of the hidden field of the managed metadata column using the below rest API 

     http://<sitecollection>/<site>/_api/web/Lists/getbytitle(List Title)/items(itemid)/fieldValuesAsText   

    Here, we have to provide the site address, List title, and item Id of one of the list items. For Example:

     https://binaryrepublik.sharepoint.com/sites/blog/_api/web/Lists/getbytitle('Blog')/items(1)/fieldValuesAsText   
    

    We will get the response from the Rest API as shown below. 


    We will use this column in our program for storing the managed metadata column's value.  

    Step 5: 

    When we use the Rest API to store values to the SharePoint List/Library we need to pass the details in the body of the rest API and to provide the details for managed metadata column we need to use the below format.

        var JobTitle = "";
        this.state.setJobTitletaxonomy.forEach((element, index) => {
          if (index == 0) {
            JobTitle += "-1;#" + element.name + "|" + element.key + ";";
          }
          else {
            JobTitle += "#-1;#" + element.name + "|" + element.key + ";";
          }
        });
        if (this.state.setJobTitletaxonomy.length > 0) {
          bodyArray["fc4c63d6c6ce4056ae9a66b7db5f5564"] = JobTitle;
        }  

    Here, we have used the setJobTitletaxonomy state for the managed metadata column. Now we can use the above body array to the body of the SharePoint rest API call. 

    Step 6: 

    Below is the detailed code for retrieving the values and submitting them to create a new item to the SharePoint List/Library. Submit method of the SPFx form: 

       //getValues() is used to retrieve the request digest value for creating items using the rest API call. 
      public getValues(site): any {
        try {
          var url = site + '/_api/contextinfo';
          return fetch(url, {
            method: "POST",
            headers: { Accept: "application/json;odata=verbose", "Content-Type": 'application/json;odata=verbose', 'Access-Control-Allow-Origin': '*' },
            credentials: "same-origin"
          }).then((response) => {
            return response.json();
          });
        } catch (error) {
          console.log("getValues: " + error);
        }
      }
      //CreteItem() is used to create an item in the SharePoint list.   
      public createItem() {
        let bodyArray = {};
        bodyArray['__metadata'] = {
          "type": "SP.Data.BlogListItem"
        },
        bodyArray['Title'] = "Ajay Varma";
        var JobTitle = "";
        this.state.setJobTitletaxonomy.forEach((element, index) => {
          if (index == 0) {
            JobTitle += "-1;#" + element.name + "|" + element.key + ";";
          }
          else {
            JobTitle += "#-1;#" + element.name + "|" + element.key + ";";
          }
        });
        if (this.state.setJobTitletaxonomy.length > 0) {
          bodyArray["fc4c63d6c6ce4056ae9a66b7db5f5564"] = JobTitle;
        }
        let jsonBodyArray = JSON.stringify(bodyArray);
        var url = this.props.siteUrl + `/_api/web/lists/getbytitle('Blog')/items()`;
        return new Promise<any>((resolve, reject) => {
          try {
            this.getValues(this.props.siteUrl).then((token) => {
              fetch(url,
                {
                  method: "POST",
                  credentials: 'same-origin',
                  headers: {
                    Accept: 'application/json',
                    "Content-Type": "application/json;odata=verbose",
                    "X-RequestDigest": token.d.GetContextWebInformation.FormDigestValue,
                  },
                  body: jsonBodyArray,
                }).then((r) => {
                  return r.status != 204 ?
                    r.json() : r.status;
                })
                .then((response) => {
                  resolve(response);
                  return response;
                })
                .catch((error) => {
                  reject(error);
                });
            }, (error) => {
              console.log(error);
              reject(error);
            });
          }
          catch (e) {
            console.log(e);
            reject(e);
          }
        });
      }
    

    Results:

    Side Panel for Taxonomy Picker:

    This is the Taxonomy Picker's Panel that retrieves the terms from the term set and displays them as shown below.

    SPFx web part: 

    Once we select and save the term for the Taxonomy Picker it will be added to the Taxonomy Picker field as shown below.

    SharePoint List: 

    The SharePoint List/Library will store the values as shown below.

    Conclusion: 

    This is how we can use the multi-value taxonomy picker. Hope this helps you. 

    If you have any questions you can reach out our SharePoint Consulting team here.