ReactHooks With Spfx

In this blog, you will learn about Reacthooks in Spfx.

Hooks Overview

 
React Hooks are a way to add React.Component features to functional components. This includes features such as:
  • State
  • Lifecycle
Hooks allow you to use React's features without classes.

What is this useState() syntax?

 
You may be unfamiliar with the useState() syntax. This syntax uses a destructuring assignment for arrays. The principle is similar to how we would pull props out an object with object destructuring.

What does useState() give us?

 
UseState gives us two variables. We can name our two variables whatever we want, just know that
  • The first variable is the value. Similar to this.state
  • The second variable is a function to update that value. Similar to this.setState
  • The final part of useState is the argument that we pass to it. The useState argument is the initial state value. In the case of our counter, we started at 0.

What's wrong with classes?

 
The React Hooks intro gives a good section on this: Classes confuse both people and machines. Essentially, classes can sometimes be confusing and be written in any number of ways. Dive into somebody else's project and you could be in for a world of different syntax and style choices. There are no plans to remove class support, we just have another way to code. By allowing classes to be converted into smaller functional components, we can break out parts of our application into smaller and more focused components.

React's Effect Hook

 
The State Hook allows us to use state in React functional components. This gets us a step closer to using functional components over class components. The next part of moving to functional components is lifecycle methods. Effects are similar to componentDidMount, componentDidUpdate, and componentWillUnmount. This is what the Effect Hook is for. Furthermore, side-effects are things you want your application to make. These include:
  • Fetching data
  • Manually changing the DOM (document title)
  • Setting up a subscription
  • Effects will run after every render, including the first render.

Running an Effect Hook only when something changes

 
Since useEffect() runs every time a component renders, how do we get it to run only once on mount? The Effect Hook can take a second argument, an array. It will look through the array and only run the effect if one of those values has changed.
  1. componentDidMount: Runs once
  2. // only run on mount. pass an empty array
  3. useEffect(() => {
  4. // only runs once
  5. }, []);
  6. componentDidUpdate: Runs on changes
  7. // only run if count changes
  8. useEffect(
  9. () => {
  10. // run here if count changes
  11. },
  12. [count]
  13. );
What about componentWillUnmount()
 
To run something before a component unmounts, we just have to return a function from useEffect()

Using State and Effects Together

There's no problem using them together! Together they can create functional components that work the same as your class components!
Open a command prompt. Create a directory for SPFx solution.
md spfx-React-Hooks
 
Navigate to the above-created directory.
cd spfx-React-Hooks
 
Run the Yeoman SharePoint Generator to create the solution.
yo @microsoft/sharepoint
 
Solution Name
Hit Enter for a default name (spfx-pnp-DropDown in this case) or type in any other name for your solution.
Selected choice - Hit Enter
 
Target for the component
Here, we can select the target environment where we are planning to deploy the client web part, i.e., SharePoint Online or SharePoint OnPremise (SharePoint 2016 onwards).
Selected choice - SharePoint Online only (latest)
 
Place of files
We may choose to use the same folder or create a subfolder for our solution.
Selected choice -  Same folder
 
Deployment option
Selecting Y will allow the app to deployed instantly to all sites and will be accessible everywhere.
Selected choice - N (install on each site explicitly)
 
Permissions to access web APIs
Choose if the components in the solution require permission to access web APIs that are unique and not shared with other components in the tenant.
Selected choice - N (solution contains unique permissions)
 
Type of client-side component to create
We can choose to create a client-side web part or an extension. Choose web part option.
Selected choice - WebPart
 
Web part name
Hit Enter to select the default name or type in any other name.
Selected choice - SpfxReactHooksCrud
 
Web part description
Hit Enter to select the default description or type in any other value.
 
Framework to use
Select any JavaScript framework to develop the component. Available choices are - No JavaScript Framework, React, and Knockout.
Selected choice -  React
 
Yeoman generator will perform a scaffolding process to generate the solution. The scaffolding process will take a significant amount of time.
Once the scaffolding process is completed, lock down the version of project dependencies by running below command.
npm shrinkwrap
 
In the command prompt, type below command to open the solution in the code editor of your choice.
code .
 
NPM Packages Used,
On the command prompt, run below command.
npm i @pnp/logging @pnp/common @pnp/odata @pnp/sp --save
 
for Pollyfills
npm install --save @pnp/polyfill-ie11
 
in SpfxReactHooksCrudWebPart.ts,
  1. import * as React from 'react';
  2. import * as ReactDom from 'react-dom';
  3. import { Version } from '@microsoft/sp-core-library';
  4. import {
  5. BaseClientSideWebPart,
  6. IPropertyPaneConfiguration,
  7. PropertyPaneTextField
  8. } from '@microsoft/sp-webpart-base';
  9. import * as strings from 'SpfxReactHooksCrudWebPartStrings';
  10. import SpfxReactHooksCrud from './components/SpfxReactHooksCrud';
  11. import { ISpfxReactHooksCrudProps } from './components/ISpfxReactHooksCrudProps';
  12. import "@pnp/polyfill-ie11";
  13. import { sp, Web } from '@pnp/sp';
  14. export interface ISpfxReactHooksCrudWebPartProps {
  15. description: string;
  16. }
  17. export default class SpfxReactHooksCrudWebPart extends BaseClientSideWebPart<ISpfxReactHooksCrudWebPartProps> {
  18. protected onInit(): Promise<void> {
  19. return new Promise<void>((resolve: () => void, reject: (error?: any) => void): void => {
  20. sp.setup({
  21. sp: {
  22. headers: {
  23. "Accept": "application/json; odata=nometadata"
  24. }
  25. }
  26. });
  27. resolve();
  28. });
  29. }
  30. public render(): void {
  31. const element: React.ReactElement<ISpfxReactHooksCrudProps > = React.createElement(
  32. SpfxReactHooksCrud,
  33. {
  34. description: this.properties.description,
  35. context: this.context
  36. }
  37. );
  38. ReactDom.render(element, this.domElement);
  39. }
  40. protected onDispose(): void {
  41. ReactDom.unmountComponentAtNode(this.domElement);
  42. }
  43. protected get dataVersion(): Version {
  44. return Version.parse('1.0');
  45. }
  46. protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
  47. return {
  48. pages: [
  49. {
  50. header: {
  51. description: strings.PropertyPaneDescription
  52. },
  53. groups: [
  54. {
  55. groupName: strings.BasicGroupName,
  56. groupFields: [
  57. PropertyPaneTextField('description', {
  58. label: strings.DescriptionFieldLabel
  59. })
  60. ]
  61. }
  62. ]
  63. }
  64. ]
  65. };
  66. }
  67. }
in HookUsestate.ts
  1. export interface IHooksuseState {
  2. MileStone?:any;
  3. Id?:any;
  4. Title?:any;
  5. }
  6. /*interface ServiceInit {
  7. mysttt: 'init';
  8. }
  9. interface ServiceLoading {
  10. status: 'loading';
  11. }
  12. interface ServiceLoaded<T> {
  13. status: 'loaded';
  14. payload: T;
  15. }
  16. interface ServiceError {
  17. status: 'error';
  18. error: Error;
  19. }
  20. export type Service<T> =
  21. | ServiceInit
  22. | ServiceLoading
  23. | ServiceLoaded<T>
  24. |IHooksuseState
  25. | ServiceError;*/
in ISpfxReactHooksCrudProps.ts
  1. import {WebPartContext} from '@microsoft/sp-webpart-base';
  2. export interface ISpfxReactHooksCrudProps {
  3. description: string;
  4. context: WebPartContext;
  5. }
in SpfxReactHooksCrud.tsx
  1. import * as React from 'react';
  2. /*import styles from './SpfxReactHooksCrud.module.scss';*/
  3. import { ISpfxReactHooksCrudProps } from './ISpfxReactHooksCrudProps';
  4. import { escape } from '@microsoft/sp-lodash-subset';
  5. import { MyListComponent } from './MyList';
  6. const UserContext = React.createContext([]);
  7. export default class SpfxReactHooksCrud extends React.Component<ISpfxReactHooksCrudProps, {}> {
  8. public render(): React.ReactElement<ISpfxReactHooksCrudProps> {
  9. return (
  10. <div >
  11. <MyListComponent />
  12. </div>
  13. );
  14. }
  15. }
in MyList.tsx
  1. import * as React from 'react';
  2. import { SpListCollection } from './mySpListCollection';
  3. export const MyListComponent = () => {
  4. const { datacol, loaddata } = SpListCollection();
  5. React.useEffect(() => {
  6. loaddata();
  7. }, []);
  8. return (
  9. <div>
  10. <DataBinding DataNewCol={datacol} />
  11. </div>
  12. );
  13. };
  14. const DataBinding = (props) => {
  15. //function DataBinding(props) {
  16. const DataNewCol = props.DataNewCol;
  17. const listItems = DataNewCol.map((data, index) =>
  18. <div key={index}>
  19. <h3>{data.Title}</h3>
  20. <p>{data.MileStone}</p>
  21. <p>{data.Id}</p>
  22. </div>
  23. );
  24. return (
  25. <div>{listItems}</div>
  26. );
  27. };

in mySpListCOllection.tsx
  1. import * as React from 'react';
  2. import { sp } from "@pnp/sp";
  3. import { IHooksuseState } from './HooksuseState';
  4. export const SpListCollection = () => {
  5. const [datacol, setdatacol] = React.useState([]);
  6. const loaddata = () => {
  7. sp.web.lists.getByTitle("ProjectStatus").items.select('Title', 'Id', 'MileStone').get().then((items) => {
  8. let result: IHooksuseState[] = [];
  9. items.forEach(element => {
  10. result.push({
  11. Id: element.Id, Title: element.Title, MileStone: element.MileStone
  12. });
  13. });
  14. return result;
  15. }) .then(resultdata => setdatacol(resultdata));
  16. };
  17. return { datacol, loaddata, setdatacol };
  18. };
Here I am using "ProjectStatus" as a list name with "MileStone,Id,Title" as Field Names. For details visit React-Hooks
Happy Coding:)