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:
- 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)
- 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 [email protected] [email protected] @fluentui/[email protected]
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!
Published on:
Learn more