【发布时间】:2020-04-22 06:31:40
【问题描述】:
我尝试更新指向azure-devops-ui/Filter 的功能组件。我正在使用返回异步响应的azure-devops-extension-sdk,以便使用此组件:
https://developer.microsoft.com/en-us/azure-devops/components/filter
Azure DevOps 上的 WorkItem 内
我已经分别使用 this.state/componentDidMount 和 useState/useEffect 编写了基于类和函数组件的代码。我关注了this SO post。
但是,我只能重新渲染状态。状态更新时,类和功能组件中的 UI 组件都不会更新。
我的代码有两个版本,它们都在等待响应并成功更新状态。但是,两者都不会等待 UI 的呈现。
通用组件:
import {
IWorkItemChangedArgs,
IWorkItemFieldChangedArgs,
IWorkItemFormService,
IWorkItemLoadedArgs,
WorkItemTrackingServiceIds,
} from "azure-devops-extension-api/WorkItemTracking";
import * as React from "react";
import * as SDK from "azure-devops-extension-sdk";
import { Header } from "azure-devops-ui/Header";
import { Page } from "azure-devops-ui/Page";
import { Filter, getKeywordFilterItem } from "azure-devops-ui/Filter";
import { IListBoxItem } from "azure-devops-ui/ListBox";
import { AggregateItemProvider } from "azure-devops-ui/Utilities/AggregateItemProvider";
import {
Filter as FilterStore,
FILTER_CHANGE_EVENT,
IFilterState
} from "azure-devops-ui/Utilities/Filter";
import { GroupedItemProvider } from "azure-devops-ui//Utilities/GroupedItemProvider";
import { groupedItems, groups, statusItems } from "./data";
export class WorkItemComponent extends React.Component<{} & ExtendedProps, any> {
private provider: AggregateItemProvider<IListBoxItem>;
private filterStore = new FilterStore();
private textoStore = "second";
private groupedProvider = new GroupedItemProvider([], [], true);
private filterItems = [
getKeywordFilterItem(this.filterStore),
{ name: "Status", id: "status", items: statusItems, filterItemKey: "status" },
{
name: "Group Items",
id: "groupItems",
items: this.groupedProvider,
filterItemKey: "groupItems"
}
];
constructor(props: {}) {
super(props);
this.provider = new AggregateItemProvider<IListBoxItem>();
this.groupedProvider.push(...groupedItems);
this.groupedProvider.pushGroups(...groups);
this.provider.push(statusItems);
this.provider.push(this.groupedProvider);
if(this.props.pdata==="first")
this.filterStore = new FilterStore(
{defaultState: { groupItems: { value: [this.props.pdata,this.textoStore] }}}
);
else
this.filterStore = new FilterStore(
{defaultState: { groupItems: { value: [this.textoStore,this.props.pdata,] }}}
);
this.filterStore.subscribe(this.onFilterChanged, FILTER_CHANGE_EVENT);
this.state = {
//currentState: ""
currentState: JSON.stringify(this.filterStore.getState(), null, 4)
};
}
public render():JSX.Element {
return (
<Page className="sample-hub flex-grow">
<Header title="Filter" />
<div className="page-content">
<Filter
filterStore={this.filterStore}
filterItems={this.filterItems}
items={this.provider}
/>
<div style={{ marginTop: "16px" }} className="monospaced-text">
<span>Current state:</span>
<span>{this.state.currentState}</span>
<span>{this.props.pdata}</span>
</div>
</div>
</Page>
);
}
private onFilterChanged = (changedState: IFilterState) => {
this.setState({
currentState: JSON.stringify(this.filterStore.getState(), null, 4)
});
this.onFilterChangedExtended(JSON.stringify(this.filterStore.getState(), null, 4))
};
private async onFilterChangedExtended(estadoActual: string) {
const workItemFormService = await SDK.getService<IWorkItemFormService>(
WorkItemTrackingServiceIds.WorkItemFormService
);
workItemFormService.setFieldValue(SDK.getConfiguration().witInputs.FieldName, estadoActual);
}
Functional 组件中第一个具有 useState 和 useEffect 的 Caller:
import { WorkItemComponent } from "./WorkItemComponent";
const WorkItemFilterAsync: React.FC = props => {
let respuestaAsync="";
const [data, setData] = React.useState<string>('');
React.useEffect(() => {
const fetchdata = async() =>{
const result = await fngetFieldName()
setData(result);
}
// Execute the created function directly
fetchdata();
}, []);
async function fngetFieldName(): Promise<string> {
const workItemFormService = await SDK.getService<IWorkItemFormService>(
WorkItemTrackingServiceIds.WorkItemFormService
);
const respuesta = workItemFormService.getFieldValue(SDK.getConfiguration().witInputs.FieldName);
respuestaAsync = (await respuesta).toString();
return JSON.stringify(respuesta);
}
return <WorkItemComponent pdata={data}/>
}
export default WorkItemFilterAsync;
第二个调用者在一个类上有componentDidMount:
import {
IWorkItemChangedArgs,
IWorkItemFieldChangedArgs,
IWorkItemFormService,
IWorkItemLoadedArgs,
WorkItemTrackingServiceIds,
} from "azure-devops-extension-api/WorkItemTracking";
import * as React from "react";
import { showRootComponent } from "../../Common";
import * as SDK from "azure-devops-extension-sdk";
import { WorkItemComponent } from "./WorkItemComponent";
//const WorkItemComponent = React.lazy (() => import('./WorkItemComponent'));
class WorkItemFilter extends React.Component{
state = {
externalData: false,
};
public componentDidMount() {
this.onLoadExtended();
}
render():JSX.Element {
return(
<div className="page-content">
{this.state.externalData ? <WorkItemComponent pdata="first"/> : <WorkItemComponent pdata="third"/>}
</div>
);
}
private async onLoadExtended() {
const workItemFormService = await SDK.getService<IWorkItemFormService>(
WorkItemTrackingServiceIds.WorkItemFormService
);
let varaux = workItemFormService.getFieldValue(SDK.getConfiguration().witInputs.FieldName);
if ((await varaux).toString()!=="")
{
this.setState({
externalData: true,
});
}
}
}
showRootComponent(<WorkItemFilter />);
这是父组件:
export function showRootComponent(component: React.ReactElement<any>) {
ReactDOM.render(component, document.getElementById("root"));
}
Azure Dev Ops Extension (azure-devops-extension.json) 的配置:
{
"contributions": [
{
"id": "work-item-filter",
"type": "ms.vss-work-web.work-item-form-control",
"description": "Custom Filter",
"targets": [
"ms.vss-work-web.work-item-form"
],
"properties": {
"name": "BHD Filter",
"uri": "dist/WorkItemFilter/WorkItemFilter.html",
"height": 600,
"inputs": [
{
"id":"FieldName",
"name": "Select the field for this control.",
"type": "WorkItemField",
"properties": {
"workItemFieldTypes": ["String", "PlainText", "HTML"]
},
"validation": {
"dataType": "String",
"isRequired": true
}
我也尝试将ReactDOM.render 放在浏览器事件中,但我不能这样做,因为 UI 扩展需要一个字段来保存数据:
【问题讨论】:
-
“但我只能重新渲染状态,而不是 UI 组件,既不在类也不在功能组件中”是什么意思。在 React 中,每当 state 或 props 更新时,都会调用 render 函数。它将被重新渲染。在功能组件中,组件将在状态或道具更新时更新。状态来自
useState钩子。我不太了解 TypeScript,但我认为您想使用useState挂钩存储来自 Promise 的数据。获取useEffect中的数据并从useState挂钩中设置状态。它会自动重新渲染。 -
亲爱的@technogeek1995:虽然我把
<Filter filterStore={this.state.filterStore};仅在<span>{this.state.currentState}</span>上更改状态、工作或更新,而不是在中。仍然使用 Button 和 onClick 事件,我更改了 state.filterStore但过滤器从不更新此值或自动检查任何值,它就像构造函数上的图像一样工作。 -
亲爱的@technogeek1995:就像 ReactDOM.render 在这两种情况下都不会等待异步函数
workItemFormService.getFieldValue的响应,即使是 Lazy 和 Suspense -
我没有使用 Lazy 和 Suspense 的经验。但是,渲染函数是非阻塞的。它永远不会等待承诺的价值。它只会使用道具或状态更改重新渲染。因此,当您获取数据时,您需要设置状态,以便组件将使用新状态重新渲染并更新值。然后你会看到值不仅随着 onClick 更新,而且当数据被 Promise 接收时更新。如果这有助于更好地解释它,我可以将其形式化为一个简单的香草 JS 答案?
-
是的,亲爱的@technogeek1995
标签: javascript node.js reactjs typescript azure