How to convert a pcf code component to a virtual control

The long-awaited 'virtual control' feature is finally in preview which means you can start to try converting your controls to be virtual - but what does this actually mean?

What are virtual code component PCF controls?

Virtual controls are probably better named React code components since this is their defining feature. Using them has the following benefits:

  1. Uses the host virtual DOM - The code component natively is added to the hosting apps 'Virtual DOM' instead of creating its own. This has performance benefits when you have apps that contain many code components. See more about the React virtual DOM: Virtual DOM and Internals – React (reactjs.org)
  2. Shared libraries - When using React and Fluent UI which is the best practice for creating code components, the libraries are bundled into the code components bundle.js using web-pack. If you have many different types of code components on your page, each with its own bundled version of these libraries it can lead to a heavy footprint, even when using path-based imports. With shared libraries, you can re-use the existing React and Fluent UI libraries that are already made available by the platform and reduce the memory footprint.

You can create a new virtual control to see this in action using the Power Platform CLI with:

pac pcf init -ns SampleNamespace -n VirtualControl -t field -npm -fw react

The key parameter is -fw react which indicates to use the new virtual control template:

But how do you convert your existing code-components to virtual controls?

If you have a code component that uses React and Fluent UI today, then you can follow the steps below to convert them and benefit from the points above. If you would prefer a video of how to do this you can check out my youtube tutorial on react virtual controls.

1. Set control-type to virtual

Inside the ControlManifest.Input.xml, update the attribute control-type on the control element from standard, to virtual

For example, from:

<control namespace="SampleNamespace" constructor="CanvasGrid" version="1.0.0" display-name-key="CanvasGrid" description-key="CanvasGrid description" control-type="standard" >

to:

<control namespace="SampleNamespace" constructor="CanvasGrid" version="1.0.0" display-name-key="CanvasGrid" description-key="CanvasGrid description" control-type="virtual" >

2. Add platform-library references

Again, inside the ControlManifest.Input.xml, locate the resources element add the platform libraries for React and Fluent. This will tell the platform that the component needs these libraries at runtime.

<resources>
      <code path="index.ts" order="1"/>
      <platform-library name="React" version="16.8.6" />
      <platform-library name="Fluent" version="8.29.0" />
</resources>

Note: It is important to ensure that the version of React and Fluent that you are using is supported by the platform.

3. Ensure you are using the same version of Fluent and React as the platform

To ensure you are using the correct versions of React and Fluent you can uninstall your previous ones and then add the specific version referenced above:

npm uninstall react react-dom @fluentui/react
npm install react@16.8.6 react-dom@16.8.6 @fluentui/react@8.29.0

Note: If you are using deep path based imports of fluent - check you are using the root library exports as I describe in a previous post - this is to ensure the exports will be picked up correctly.

4. Implement ComponentFramework.ReactControl

The key part of the change to index.ts is that now we must implement a new interface ComponentFramework.ReactControl<IInputs, IOutputs>  instead of ComponentFramework.StandardControl<IInputs, IOutputs>

Locate the class implementation in index.ts and update the implements interface to be:

export class YourControlName implements ComponentFramework.ReactControl<IInputs, IOutputs>

5. Update the signature of updateView

The old method signature of updateView returned void, but now you must return a ReactElement so that it can be added to the virtual DOM of the parent app. Update the signature to be:

updateView(context: ComponentFramework.Context<IInputs>): React.ReactElement

6. Remove ReactDOM.render

Since we are using the virtual DOM of the parent app, we no longer need to use ReactDOM. You will normally have code similar to:

ReactDOM.render(React.createElement(MyComponent)
),this.container);

Replace this now with simply:

return React.createElement(MyComponent);

7. Remove calls to unmountComponentAtNode

Previously you would have had to dismount the React Virtual DOM elements in the code component's destroy method. Locate the destroy method and remove the line:

ReactDOM.unmountComponentAtNode(this.container);

8. Make sure you are using the latest version of the Power Apps CLI

To ensure that your Power Apps CLI supports virtual controls, ensure it is updated to the latest version. I recommend doing this using the VSCode extension if you are not already using it and removing the old MSI installed version. You will need to do a npm update pcf-scripts pcf-start to grab the latest npm modules that support react virtual controls!

That's it!

It really is that simple. If you now use npm start watch you'll see your component rendered, but the bundle.js size will be smaller and when you deploy, it'll be faster in apps that contain many components.

Check out the official blog post about this feature for more info.

Hope this helps!

@ScottDurow

 

Pingbacks and trackbacks (1)+

Comments are closed