Tuesday, 26 May 2020

Create a custom React hook to mimic class component's setState behaviour

I have been playing around with react hooks recently, and slowly wrapping my head around how they work. Hooks are a great way to create reusable functionality, but there are some instances where I think class components fared better. Let's talk about one such scenario, and also how to get around it when using hooks.


When working with react state in a class component, if we wanted to update a specific property on the state object, all we had to do was call the setState method and pass in an object containing only the updated property and value. The resultant state would be the previous state merged with the new property value.

It works a bit differently with hooks. When using the default useState hook, the new object passed in entirely replaces the previous state. So all the properties in the previous state are overwritten. So every time you want to update a state object, the onus is on the developer to keep track of the previous state and update the new state based on that. Here is how this behaviour manifests in code:

As you can see in the code the value of the FirstName property is lost when we update the LastName property. 

There is a way to set the state based on previous state by using the functional update pattern: https://reactjs.org/docs/hooks-reference.html#functional-updates

But that means that every time we used to just use this.setState in class components, we now have to use

setState(prevState => {
  return {...prevState, ...updatedValues};
});

This would be additional overhead for us devs which I wanted to check if we could avoid.

Fortunately with the reusable nature of hooks, we can simply create a custom hook which mimics the class components setState behaviour and merges the new state with the previous state. And we can use this custom hook any time we want to merge the state.

Here is how the previous code would look when using our custom hook:


The only change in code is that we have replaced react's useState with our custom useCustomState. This gives us back the ability to merge state objects instead of replacing them. (I have also changed the variable names from state and setState to customState and setCustomState respectively to make it explicit that we are using the custom hook. But you can just as well keep using the state and setState names)

So how does our custom hook work? It's built as a wrapper on top of the useState hook's functional update pattern. Any time a state object is passed to the setCustomState function, it internally uses the functional update pattern and merges the new state with the previous state. Let's have a look at the code:


This automates the overhead of using the functional pattern. It is no longer the the developers responsibility, and instead, is done by the custom hook.

But wait, what if there is a scenario where a new state depends on the previous state? Our custom hook does not yet expose a way for the developer to update the state based on the previous state. Let's fix that. We can update our hook to add a method which accepts a function. This function will receive the previous state from react.  

And consuming the new hook can be done by passing in a function updater: 

Hope that helps! For the sake of completeness here is the full code for our custom hook:

Full solution including the custom hook and the consuming code can be found here: https://github.com/vman/spfx-react-hooks-customstate

This post generated some good discussion on twitter with Yannick Plenevaux regarding the ideal cases when to use this approach as opposed to other approaches of state management like using the useState or useReducer hooks. Have a look here: https://twitter.com/yp_code/status/1265244244077416448 

Monday, 27 April 2020

SPFx Teams tab: Use react hooks to dynamically handle theme changes

When developing a custom Teams tab with the SharePoint Framework, we want to make sure that the SPFx custom tab (web part) is styled according to the current selected theme.

Also, if the user switches the theme (e.g. from default to dark), we want to make sure that the styling of our SPFx web part also changes without the user having to reload the tab.


Let's see how we can do this. In this post, depending on the Teams theme, we will dynamically add a CSS class to our top level react component. And when the theme changes, we will change this class as well. That will be the scope of this post without going into further details of Styling/CSS.

Also, since I have been exploring React hooks recently, we will be using them in the demo code.

The very first thing we need to do is to make sure that our SPFx tab loads with the currently selected theme when the tab loads for the first time.. This can be achieved with the teamsJS sdk bundled with SPFx:

The theme property will either have the values "default", "dark" or "contrast" (at the time of writing this post). Depending on this property, we can add a CSS class to the top level container of our web part.

Now the important part. When the user updates the theme, we want to fire an event which can be used to set the new theme in our tab. Luckily the teamsJS sdk provides us with a function which can be used to register this event handler:

https://docs.microsoft.com/en-us/microsoftteams/platform/tabs/how-to/access-teams-context#theme-change-handling

With this, we are able to capture the theme update dynamically and update our component state. Next, we also want to change the CSS class of the top container when the theme changes. We will do this by using the useEffect hook again and setting the class depending on the selected theme.

This hook takes in the themeState as a dependency which means that it will run anytime themeState changes. We will use it to update the styleState.

I could have been a bit more clever here and simply updated the class in the registerOnThemeChangeHandler function itself. But I wanted to keep the themeState and styleState separate to have them loosely coupled and also to play around with hooks a bit more ;)

Here is the complete code for the React Functional component:

Hope you found the post useful! As always, the code for this post is available on GitHub: https://github.com/vman/spfx-teams-theme-hooks

Thursday, 5 March 2020

Office 365 CLI: Grant admin consent to specific scopes using Microsoft identity platform (AAD v2)

The Office 365 CLI is great tool for a variety of scenarios. Specifically, I find it super useful for CI/CD and automation. Since there is no official Microsoft Graph Azure DevOps task yet, the Office 365 CLI comes to the rescue when we want to make a call to the Graph from an Azure DevOps pipeline. Most recently, I was using it to deploy a Teams app when I noticed something interesting.

As I was calling the Office CLI from an automated script, I could not use the default deviceCode flow to login as there was no user interaction possible. So the other options were to use the username/password flow or use a custom certificate. I chose the former as it was very convenient and I could store the password securely in a Azure DevOps secret variable.

Now before using the Office CLI on a tenant, we need to consent to the AAD multi-tenant app used internally for authenticating to Office 356 services. This app is called "PnP Office 365 Management Shell" and the consent process is also described in the docs here: https://pnp.github.io/office365-cli/user-guide/connecting-office-365/

If this consent hasn't happened yet, we get the following error:

Error: AADSTS65001: The user or administrator has not consented to use the application with ID '31359c7f-bd7e-475c-86db-fdb8c937548e' named 'PnP Office 365 Management Shell'. Send an interactive authorization request for this user and resource.

If we go ahead with the default consent experience, we are presented with this screen where the CLI AAD app requests a large number of permissions on the tenant:


Now in my case, I did not need all the scopes the CLI was requesting. I only needed AppCatalog.ReadWrite.All to deploy my Teams app.

So that got me thinking, one of the benefits of using the Microsoft identity platform (also called AAD v2) is that apps can request consent to specific scopes:

With the Microsoft identity platform endpoint, you can ignore the static permissions defined in the app registration information in the Azure portal and request permissions incrementally instead, which means asking for a bare minimum set of permissions upfront and growing more over time as the customer uses additional app features. To do so, you can specify the scopes your app needs at any time by including the new scopes in the scope parameter when requesting an access token - without the need to pre-define them in the application registration information.
https://docs.microsoft.com/en-us/azure/active-directory/azuread-dev/azure-ad-endpoint-comparison#incremental-and-dynamic-consent

So armed with this knowledge, I tested whether I could request (and grant) only AppCatalog.ReadWrite.All to the PnP Office 365 Management Shell app.

Turns out it is indeed possible by going to this URL in a browser as an admin:

You will notice that in the client_id param, we are specifying the client id of the PnP Office 365 Management Shell app and in the scope param, we are only requesting the needed scope i.e. AppCatalog.ReadWrite.All


This time, we are presented with a significantly reduced scopes for consent. Notice only the "Read and write to all app catalogs" permission is present along with the couple of default permissions.

After granting the consent and then navigating to Azure AD -> Enterprise Applications -> All applications ->  PnP Office 365 Management Shell -> Permissions, you will see that only the requested scope was granted:


This way, you can only request the scopes you need and also include multiple scopes by separating them with a space

Automating the consent:


If you are like me and would like to automate the consent process as well, that is possible by using the Azure CLI:

00000003-0000-0000-c000-000000000000 is the Application ID of the Microsoft Graph resource in AAD. This will be the same for all tenants.

Hope you found the post useful!

Monday, 24 February 2020

Microsoft Bot Framework v4: Send proactive messages to Teams channels and users

What is a Bot Framework Proactive message?

Usually, for starting a conversation with a Microsoft Teams bot, the user has to initiate the conversation either by sending a personal message to the bot, or by mentioning the bot in a Teams channel or by invoking a messaging extension.

With proactive messaging, the bot can start a conversation with a user (or in a Teams channel) without anyone having to invoke the bot first. This conversation can be started based on any custom logic fit for your application e.g. The occurrence of  an external event, or a webhook getting triggered or even on a scheduled periodical basis.

More about Bot Framework proactive messages here: https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0&tabs=csharp

I should mention that the bot will first need to be installed in the Team, if you want to send a proactive message to a Teams channel, or to the users who are part of that team.



How to send proactive messages?

So in this post, let's look at a few code samples which make it very easy for our Teams Bot to send proactive messages to users or channels in a Team.

These code samples are based on a standalone .NET Core console app. This is mainly to show that as long as you have the necessary information, your code doesn't need to be running under the Bot messaging endpoint. Once you have the information from the Bot messaging endpoint, the proactive messaging code can run from any platform e.g. from an Azure Function.

If you have a look at the Bot Framework code samples published by Microsoft, they all use code which is running under the messaging endpoint. This initially led me to believe that even for proactive messaging, the code should live under the same endpoint. But as we will see in this post, that is not the case.

What are the prerequisites?

As mentioned before, our bot will need to be installed in a Team first. This will allow the bot messaging endpoint to receive the required values from Teams and send it to our proactive messaging code. If the bot is not installed in the Team, you will get a "Forbidden: The bot is not part of the conversation roster" error.

Base URL (serviceUrl)

This is the Url to which our proactive messaging code should send all the requests. This Url is sent by Teams in the Bot payload in the turnContext.Activity.serviceUrl property. For all intents and purposes this url will remain constant but after having a discussion with Microsoft, they have recommended that this url might change (very rarely) and our bot should have the logic for updating the stored base url periodically from the payload sent to the bot. More about the Base Url here: https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference?view=azure-bot-service-4.0#base-uri

Internal Team Id

This is the internal team id which is not the same as the Office 365 Group Id. The internal team id is in the following format: 19:bf2b184e7cbb4f9f9ca1b47f755cd943@thread.skype

You can get the internal team id from the Bot payload in the channelData.team.id property. You can also get this id through the Microsoft Graph API: https://docs.microsoft.com/en-us/graph/api/resources/team?view=graph-rest-1.0#properties

Channel Id

If we want our bot to post to a specific channel in a Team, then we will need the channel id as well. The format for the channel id is exactly the same as the internal team id. Also, you can get the channel id from the bot payload as well as the Microsoft Graph api: https://docs.microsoft.com/en-us/graph/api/resources/channel?view=graph-rest-1.0#properties

Internal Teams User Id

This would only be needed if you want to send a proactive personal message to a specific user. For all users in a team, Teams maintains an encoded user id so that only bots installed in a team are able to message users. To get this user id, our bot needs to call the conversations/{conversationId}/members REST API endpoint. Fortunately for us the Bot Framework wraps this call in a handy SDK method as shown in the third code sample below.

So once we have all the required values from the Bot messaging endpoint, we are able to send proactive messages. For this sample code, I am using the Microsoft.Bot.Builder v4.7.2
https://www.nuget.org/packages/Microsoft.Bot.Builder/

1) Post a proactive message in a Teams channel



(click to zoom)

2) Post a proactive message in a Teams channel and mention a user in it



(click to zoom)


3) Post a proactive personal message to a user


(click to zoom)

Hope you found the post useful!

Wednesday, 5 February 2020

SPFx: Using React hooks to globally share service scope between components

In my previous posts, I have written quite a few times about SharePoint Framework service scopes (I will add links at the end of the article). In short, Service Scopes are the SPFx implementation of the Service Locator pattern i.e. a single shared "dictionary" where services (either oob SPFx or custom) are registered and can be consumed from any component in the application.

For example, without using service scopes, if we wanted to make a call to the Microsoft Graph (using MSGraphClient) from within a deeply nested react component, either we would have to pass in the SPFx context down all the components in the tree, or maybe a create a custom service which returns the web part context, and then call that service from within our nested component. Or maybe use redux to globally maintain the context in a single state object.

But with all these approaches (there may be more), testing the components would be difficult as they would have a dependency on the SPFx context which is hard to mock. Waldek Mastykarz has a great post on this.

Also, from a maintenance point of view, it could get tricky as almost all our components would start to depend on the entire context and we could easily loose track of which specific service from the context is needed by the component.

Now with my previous posts on service scopes, even though we were removing the dependency on the SPFx context, one issue still remained that the SPFx service scope was still needed to be passed into the component. We were just replacing the SPFx context with the SPFx service scopes. While this was good from a testing point of view, it wasn't great for maintainability.

Fortunately, in the recent versions of SPFx, React 16.8+ was supported which means that we can take advantage of React hooks. Specifically, the useContext hook. This gives us a very straightforward way to store the SPFx service scope in the global react context (which is different to the SPFx context) and then consume it from any component in our application no matter how deeply nested it is.

Let's see how to achieve this. In these code samples, I am using SPFx v1.10 which is the latest version at the time of writing.

1) The Application Context object


First, we need to create the React application context object which will be used to store and consume the service scope. For now I am only storing the serviceScope in the context. Other values can be stored here as well.


2) React Higher Order Component (HOC)


React hooks can only be used from functional components and not classes. With the SPFx generator creating classes by default and hooks being fairly new, I am sure there is a lot of code out there already which use classes and not functional components. Changing all code to use functional components instead of classes is a non-starter.

Fortunately, there is a way to use react hooks with classes by creating a Higher Order Component (HOC) which is a functional component. We can wrap all our class components with this HOC and safely consume the useContext hook from within this component.

(Update: If you are interested in going down the "full hooks" approach and doing away entirely with classes, Garry Trinder has got you covered. He has created a fork which only uses functional components and hooks so we don't need the HOC. If you want to take this approach, check out the code here: https://github.com/garrytrinder/spfx-servicescopes-hooks)


3) SPFx web part 


Next, we update our SPFx webpart to only pass in the serviceScope once to our top level component:


4) Top level React component 


Our top level component will need to be wrapped with the AppContext so that any nested component will be able to consume it. This just needs to be done once on the top level react component. You will notice that the HelloUser child component does not need any props passed in.


5) Child component  


Due to the Higher Order component and the useContext hook, we are able to access the serviceScope property from withing the child component. We can grab the MSGraphClient from the serviceScope and start making calls to the Graph:

And that's it! This way, we can use the React useContext hook to globally share our SPFx service scope.

Hopefully you have found this post helpful! All code is added in the GitHub repo here: https://github.com/vman/spfx-servicescopes-hooks


Also, if you are interested, here are all my previous articles on SPFx service scopes:

SharePoint Framework: Org Chart web part using Office UI Fabric, React, OData batching and Service scopes

Getting the current context (SPHttpClient, PageContext) in a SharePoint Framework Service

Service Locator pattern in SPFx: Using Service Scopes

Service Locator pattern in SPFx: Using nested scopes to work with multiple components

Wednesday, 15 January 2020

Create Microsoft Teams manifest manually for Personal app powered by SPFx

SPFx 1.10 was released recently which now includes support for Teams personal apps. Catch the announcement here: https://developer.microsoft.com/en-us/sharepoint/blogs/announcing-sharepoint-framework-1-10-extending-sharepoint-framework-across-microsoft-365/

To deploy a personal app with an SPFx package, you have the option of deploying the package to the SharePoint tenant app catalog and clicking on the "Sync to Teams" button which then makes the app available in Teams as shown here: https://docs.microsoft.com/en-us/sharepoint/dev/spfx/integrate-with-teams-introduction

But what if you are coming at it from the Teams app point of view? You already have a Teams app with a bot or a messaging extension and want to add the SPFx powered personal app to it. You probably don't want to use the "Sync to Teams" option in this case because then your SPFx web-part will be available as a separate app in the Teams app catalog.

Fortunately, it's very simple to define a staticTab in the Teams manifest which points to the SPFx webpart. This then makes the SPFx webpart available as a teams personal app:

Notice the teams and personal query string parameters as they are very important. You will also have to replace the component id with the id of your web-part.

I have also updated the official MS Docs with this approach:
https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/guidance/creating-team-manifest-manually-for-webpart

Thanks to my colleague Jarbas for working with me in figuring out this one!

Monday, 2 December 2019

SharePoint Framework Web Part and Property Pane Lifecycles

I was working on a SharePoint Framework webpart, especially on the property pane, and needed to understand when the SPFx WebPart lifecycle methods are executed. Specifically, the order in which they are fired.

I tried searching for this but couldn't find much information, so thought of creating this post as it might be helpful for other who might be looking for it.

The Microsoft docs on these methods/APIs are fairly extensive and they give a good overview of what each method does:
https://docs.microsoft.com/en-us/javascript/api/sp-webpart-base/baseclientsidewebpart?view=sp-typescript-latest#methods
https://docs.microsoft.com/en-us/javascript/api/sp-webpart-base/basewebpart?view=sp-typescript-latest

What is missing though is the order in which these methods are fired. So this post should serve as a nice complement to the docs.

I should mention that this post only focuses on the native SPFx lifecycle methods. If you are creating an SPFx webpart with React for example, the react lifecycle is out of scope for this post as there is loads of information available already on the interwebs.


SPFx webpart method execution order:


When loading the web part on a page, the methods are fired in the following order:


1) protected onAfterDeserialize(deserializedObject: any, dataVersion: Version): TProperties;

2) protected onInit(): Promise<void>;

3) protected render(): void;

4) protected onBeforeSerialize(): void;

When the web part is removed from a page, the methods are fired in the following order:


1) protected onDispose(): void;


SPFx webpart property pane method execution order:


Opening the property pane:

1) protected loadPropertyPaneResources(): Promise<void>

2) protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration;

3) protected onPropertyPaneRendered(): void;

4) protected onPropertyPaneConfigurationStart(): void;


Updating the properties in the property pane:


The SPFx property pane can be set in either the reactive mode or in a non-reactive mode:

"Reactive implies that changes made in the PropertyPane are transmitted to the web part instantly and the user can see instant updates. This helps the page creator get instant feedback and decide if they should keep the new configuration changes or not.

NonReactive implies that the configuration changes are transmitted to the web part only after "Apply" PropertyPane button is clicked."

When in reactive mode, if any property is changed, the methods are fired in the following order:


1) protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void;

2) protected render(): void;

3) protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration;

4) protected onPropertyPaneRendered(): void;

5) protected onPropertyPaneConfigurationComplete(): void;



When in non-reactive mode, if any property is changed, the methods are fired in the following order:


1) protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void;

2) protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration;

3) protected onPropertyPaneRendered(): void;


When in non-reactive mode, after clicking on the "Apply" button, the methods are fired in the following order:

1) protected onAfterPropertyPaneChangesApplied(): void;

2) protected render(): void;

3) protected onPropertyPaneConfigurationComplete(): void;

4) protected onPropertyPaneRendered(): void;


When the property pane is closed by clicking on the "X" button

1) protected onPropertyPaneConfigurationComplete(): void;


Hope you found the post helpful! Let me know if I might have missed any. Would love to update this post in the future.