SharePoint Framework and React hooks. Should I care?

SharePoint Framework 1.9 introduced support for React 16.8+. While only a minor part of the version was changed (16.7 -> 16.8), it means a lot. It means that you can use the full power of React hooks. But should you? Obviously, the answer is yes, because React hooks introduce a lot of useful features, including:

  • reuse stateful logic across your many React components, which isn't possible with class-based components
  • get rid of high-order components, you can move some logic out of your React components into custom hooks. All that makes your code cleaner
  • all your React components now are in the same style (you don't mix class-based components with functional). Instead, you use only functional components, because they support state (with help of hook of course)

There are even more reasons going to hooks, instead of class-based components. Check out official documentation from React:

Should I use Hooks, classes, or a mix of both?

....we’d encourage you to start trying Hooks in new components you write. ....In the longer term, we expect Hooks to be the primary way people write React components.

Hooks are available starting from February 2019. A lot of libraries adopted their code to hooks. You are on the safe side if you're planning to use hooks in your code today.

React hooks and SharePoint Framework

But what about SharePoint Framework? Your SharePoint Framework code will benefit from all the good things coming from React hooks I mentioned above. Additionally, you can write custom hooks in order to reuse or share some SharePoint Framework related functionality.

Vardhaman Deshpande has written a very interesting article on how to share service scope between components with hooks. Then Garry Trinder adopted his solution to be hooks-only. Then I created a custom hook based on Garry's solution :). Also, check out a great example of custom hooks in SPFx by the newly awarded Office Development MVP Rabia Williams.

React hook to inject any context property from parent SharePoint Framework web part

This is an example of what you can do with hooks and SharePoint Framework. Below is a custom hook, which returns any part of a web part's context: 

import * as React from "react";

import { WebPartContext } from "@microsoft/sp-webpart-base";
import { useContext } from "react";

import AppContext from "../common/AppContext";

export type UseWebPartContextReturn = (() => WebPartContext) &
  (<A>(mapContext: (context: WebPartContext) => A) => A);

const useWebPartContext: UseWebPartContextReturn = (mapContext?: (context: WebPartContext) => any) => {
  const context = useContext(AppContext);

  return mapContext ? mapContext(context) : context;
};

export { useWebPartContext };

How to use it:

import * as React from 'react';
import { FC } from 'react';

import { IHelloWorldProps } from './IHelloWorldProps';
import { useWebPartContext } from '../../../hooks/useWebPartContext';
import { MSGraphClientFactory } from '@microsoft/sp-http';
import ClassComponent from "./ClassComponent";

const HelloWorld: FC<IHelloWorldProps> = (props) => {
  const [name, setName] = React.useState('');
  /*
  * Use cases:
  */

  // get single property
  const webPartId = useWebPartContext(context => context.instanceId);

  // or complex object
  const ctx = useWebPartContext(context => ({
    webPartId: context.instanceId,
    loginName: context.pageContext.user.loginName,
    msGraphClientFactory: context.serviceScope.consume(MSGraphClientFactory.serviceKey)
  }));

  // or just the whole context
  const wpContext = useWebPartContext();

  // get data using ms graph:
  React.useEffect(() => {
    async function process() {
      const client = await ctx.msGraphClientFactory.getClient();
      client
        .api('/me')
        .get((error, user: any, rawResponse?: any) => {
          setName(user.displayName);
        });
    }

    process();
  }, []);

  return <div>
    <div>
      Legacy page context: <pre>{JSON.stringify(wpContext.pageContext.legacyPageContext)}</pre>
    </div>
    <div>
      Web Part id: {webPartId}
    </div>
    <div>
      Login name: {ctx.loginName}
    </div>
    <div>
      User name: {name}
    </div>
  </div>;
};

export default HelloWorld;

In your React component file, simply import useWebPartContext, then use it as a regular React hook, you can provide map function to return only a subset of context (good for unit testing). 

Conclusion

In your new SPFx code, targeted for SharePoint Framework 1.9+ use React hooks, because they have a number of cool features. You can even create custom hooks for your SharePoint web parts in order to reuse or share logic. 

As usual, the sample is on the GitHub here (forked version).