如何解决受控 Fluent UI Checkbox 组件的渲染不正确
我有一个通过 REST API 与数据库交互的表单,并提供了几个受控的 Fluent UI 组件。对于多选字段,我构建了一个组件,该组件显示具有任意数量受控复选框组件的 Stack。下面是组件定义。
class MultiChoiceField extends React.Component
{
static contextType = FormContext;
static displayName = "MultiChoiceField";
#handlers = { change: {} };
/**
* Initializes the component using the information provided in the {@link Item} provided by the {@link FormContext}.
* @constructor
* @param {Object} props The properties provided for this component.
*/
constructor(props)
{
super(props);
this.state = { value: {} };
}
/**
* Set up the component once it is added to the DOM. Context isn't available in the constructor,so we set up the
* value here.
* @function
* @param {Object} nextProps The value that will be assigned to `this.props`.
* @param {Object} nextContext The {@link FormContext} that will be assigned to `this.context`.
* @public
* @returns {void}
*/
componentDidMount(nextProps,nextContext)
{
const choices = nextProps?.Field?.Choices?.results || [];
let value = nextContext?.Item?.[nextProps.FieldName] || {};
value = Array.isArray(value) ? value : (value.results || []);
this.setState({
value: choices.reduce((result,choice) => ({ ...result,[choice]: value.indexOf(choice) >= 0 }),{})
});
}
/**
* Update the component when it receives new props or context information.
* @function
* @param {Object} nextProps The value that will be assigned to `this.props`.
* @param {Object} nextContext The {@link FormContext} that will be assigned to `this.context`.
* @public
* @returns {void}
*/
componentwillReceiveProps(nextProps,nextContext)
{
const choices = nextProps?.Field?.Choices?.results;
let value = nextContext.Item?.[nextProps.FieldName] || {};
value = Array.isArray(value) ? value : (value.results || []);
this.setState({
value: choices.reduce((result,{})
});
}
/**
* Get an event handler for the specified choice.
* @function
* @param {string} name The choice with which this event handler is associated.
* @public
* @returns {function} An event handler for the specified choice.
*/
handleChange = (name) =>
{
const bubbleOnChange = (event,value) =>
(this.props.onChange?.(event,Object.keys(value).filter((choice) => (value[choice]))));
if (!this.#handlers.change[name])
{
this.#handlers.change[name] = (event) =>
{
const value = { ...this.state.value,[name]: !this.state.value[name] };
this.setState({ value },() => (void bubbleOnChange(event,value)));
};
}
return this.#handlers.change[name];
}
/**
* Render the user interface for this component as a
* [Stack]{@link https://developer.microsoft.com/en-us/fluentui#/controls/web/stack} containing
* [CheckBox]{@link https://developer.microsoft.com/en-us/fluentui#/controls/web/checkBox} components.
* @function
* @public
* @returns {JSX} The user interface for this component.
*/
render()
{
const choices = this.props.Field.Choices.results;
return (<>
<Fabric.Stack {...this.props.stackTokens}>
{choices.map((choice) => (
<Fabric.CheckBox label={choice} checked={this.state.value[choice]}
onChange={this.handleChange(choice)} key={choice} />
))}
</Fabric.Stack>
<div
className="errorMessage"
id={`FormFieldDescription--${this.context.Item?.Id}__${this.props.FieldName}`}
role="alert"
style={{ display: this.props.errorMessage ? "" : "none" }}>
{this.props.errorMessage}
</div>
</>);
}
}
表单通过 REST API 检索数据后,此组件使用该数据更新其状态。虽然状态已正确更新并且正确的值被传递到每个 CheckBox 组件的 props
,但 UI 具有误导性。例如,根据 React,下面的 checked
值分别设置为 false
、true
、false
、false
和 false
Chrome DevTools 中的组件检查器。
显然,虽然 props
设置正确,但用户会看到五个未选中的复选框。当用户点击本应勾选的复选框时,state
会正确更新以反映所有五个复选框都未勾选。这是用户点击第二个复选框后的样子。
用户与 CheckBox 组件交互,它们的行为与预期一致,但对于初始 checked
属性设置为 true
的任何地方,基础值都完全反转。
解决方法
我在构造函数中添加了 context
但这没有帮助,所以我将这个类组件转换为函数组件。它按预期工作。这是功能组件的代码。
const MultiChoiceField = ({ errorMessage = "",Field,FieldName,onChange,stackTokens = {} } = {}) =>
{
const context = useContext(FormContext);
const [value,setValue] = useState({});
const handleChange = (choice) => (event) =>
{
const bubbleOnChange = (event,value) => (void onChange?.(event,value));
const getValueAsArray = (valueNew) =>
(Object.entries(valueNew).filter(([,value]) => (value)).map(([key]) => (key)));
const valueNew = { ...value,[choice]: !value[choice] };
bubbleOnChange(event,getValueAsArray(valueNew));
};
const updateChoices = () =>
{
const reduceSelected = (valueContext) => (result,choice) =>
({ ...result,[choice]: ~valueContext.indexOf(choice) });
const choices = Field?.Choices?.results || [];
let valueContext = context?.Item?.[FieldName] || {};
valueContext = Array.isArray(valueContext) ? valueContext : (valueContext.results || []);
setValue(choices.reduce(reduceSelected(valueContext),{}));
};
useEffect(updateChoices,[Field?.Choices,context?.Item]);
const renderChoice = (choice) => (
<Fabric.Checkbox checked={!!value[choice]} key={choice} label={choice} onChange={handleChange(choice)} />
);
return (<>
<Fabric.Stack {...stackTokens}>{(Field?.Choices?.results || []).map(renderChoice)}</Fabric.Stack>
<div
className="errorMessage"
id={`FormFieldDescription--${context.Item?.Id}__${FieldName}`}
role="alert"
style={{ display: errorMessage ? "" : "none" }}>
{errorMessage}
</div>
</>);
};
MultiChoiceField.displayName = "MultiChoiceField";
注意接口是一样的,state
的内部存储本质上是一样的。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。