Hi all
I am loosing my mind with this issue - hope anyone can help me.
I have developed a React PCF control that is bound to a SingleLine.Text field. The control takes a string as input (parameter name is "value") and hides the 4 last characters of the string. The control has an associated toggle that can show/hide the last 4 characters of the string.
Here is the ControlManifest.Input.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<manifest>
<control
namespace="FieldAnonymizerComponent"
constructor="FieldAnonymizerComponent"
version="0.0.1"
display-name-key="FieldAnonymizerComponent"
description-key="FieldAnonymizerComponent description"
control-type="virtual"
>
<external-service-usage enabled="false">
</external-service-usage>
<property name="value" display-name-key="value" description-key="value" of-type="SingleLine.Text" usage="bound" required="true" />
<resources>
<code path="index.ts" order="1"/>
<platform-library name="React" version="16.8.6" />
<platform-library name="Fluent" version="8.29.0" />
</resources>
</control>
</manifest>
Here is the index.ts file:
import { IInputs, IOutputs } from "./generated/ManifestTypes";
import AnonymizerComponent, { IAnonymizerComponentProps } from "./AnonymizerComponent";
import * as React from "react";
export class FieldAnonymizerComponent implements ComponentFramework.ReactControl<IInputs, IOutputs> {
private _notifyOutputChanged: () => void;
private _value: string;
private _isReadOnly: boolean;
private _refreshData: (value: string) => void;
constructor() { }
public init(
context: ComponentFramework.Context<IInputs>,
notifyOutputChanged: () => void,
state: ComponentFramework.Dictionary
void {
this._value = context.parameters.value.raw ?? "";
this._isReadOnly = context.mode.isControlDisabled;
this._notifyOutputChanged = notifyOutputChanged;
this._refreshData = this.refreshData.bind(this);
}
public updateView(context: ComponentFramework.Context<IInputs>): React.ReactElement {
const props: IAnonymizerComponentProps = {
value: this._value,
isReadOnly: this._isReadOnly,
refreshData: this._refreshData
};
return React.createElement(
AnonymizerComponent, props
);
}
public refreshData(value: string): void {
this._value = value;
this._notifyOutputChanged();
}
public getOutputs(): IOutputs {
return {
value: this._value
};
}
public destroy(): void { }
}
Here is the React component:
import * as React from 'react';
import { Stack } from '@fluentui/react';
import { useState } from 'react';
import { provideFluentDesignSystem, fluentTextField, fluentSwitch } from '@fluentui/web-components';
import { provideReactWrapper } from '@microsoft/fast-react-wrapper';
import { Constants } from './../constants/constants';
const { wrap } = provideReactWrapper(React, provideFluentDesignSystem());
export const FluenTextField = wrap(fluentTextField());
export const FluentSwitch = wrap(fluentSwitch());
export interface IAnonymizerComponentProps {
value?: string;
isReadOnly: boolean;
refreshData: (value: string) => void;
}
const AnonymizerComponent = (props: IAnonymizerComponentProps) => {
const [isAnonymized, setIsAnonymized] = useState(true);
const [value, setValue] = useState(props.value ?? "");
return (
<Stack horizontal>
<FluenTextField
value={isAnonymized ? anonymize(value) : value}
style={{ width: "100px" }}
onInput={(e: React.FormEvent<HTMLInputElement>) => updateValue(e)}
readOnly={setIsReadOnly()}
maxLength={Constants.MaxCprLength}
/>
<Stack verticalAlign="end">
<FluentSwitch
onClick={() => { isAnonymized ? setIsAnonymized(false) : setIsAnonymized(true) }}
style={{ marginLeft: "10px", marginBottom: "6px" }}
>
<span slot="checked-message" style={{ paddingLeft: "7px" }}>{Constants.ToggleLabel}</span>
<span slot="unchecked-message" style={{ paddingLeft: "7px" }}>{Constants.ToggleLabel}</span>
</FluentSwitch>
</Stack>
</Stack>
)
function updateValue(event: React.FormEvent<HTMLInputElement>): void {
setValue(event.currentTarget.value);
props.refreshData(event.currentTarget.value);
}
function anonymize(value?: string): string {
if (!value) return "";
if (value.length == Constants.MaxCprLength) {
return `${value.slice(Constants.CprSubSetMinValue, Constants.CprSubSetMaxValue)}${Constants.CprAnonymizedSuffix}`
}
return value;
}
function setIsReadOnly(): boolean {
if (props.isReadOnly) return true;
else return isAnonymized;
}
}
export default AnonymizerComponent;
The PCF control works fine when added to a form in a model driven app.
Toggle off:
Toggle on:
However, when I try to add it to a Canvas App and apply the input for the "value" parameter using a variable, like so:
Nothing is shown in the control, even though I know that the variable contains the value "1111111111":
If I provide static value like so:
Then the value is displayed in the control:
I hope I have provided enough information and that someone can help me with this very strange issue. Thanks!
Thanks for the suggestion. I added it to my component and I also modified the updateView to set the value property using the context parameter that is provided to the function, instead of the private class variable. This fixed the issue.
Hello,
I believe issue with the behavior of your component is that when the value is changed externally - I mean after it was rendered for the first time (doesn't matter from Model Driven App or from Canvas App) you don't update the state inside your component. In order to do that you can use useEffect hook. Try the following code:
React.useEffect(() => {
setValue(props.value ?? "");
}, [props.value]);
WarrenBelz
87
Most Valuable Professional
mmbr1606
71
Super User 2025 Season 1
Michael E. Gernaey
65
Super User 2025 Season 1