[英]How to re-render React Component when promise resolves? | How to block render until data loads?
我試圖更新一個指向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
編寫了基於類和 function 組件的代碼。 我關注了這個 SO 帖子。
但是,我只能重新渲染 state。 當 state 更新時,class 和功能組件中的 UI 組件都不會更新。
我的代碼有兩個版本,都在等待響應並成功更新state。 但是,兩者都不會等待 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);
}
第一個調用者在函數組件中使用 useState 和 useEffect :
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;
第二個調用者在 class 上使用 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 擴展需要一個字段來保存數據:
使用新的 React Hooks 編寫功能性反應組件很簡單。 在下面的示例中,我使用的是useState
和useEffect
。 useState
鈎子與基於類的 React 組件中的this.state/this.setState
同義。 useEffect
鈎子類似於componentDidMount
+ componentDidUpdate
。 它也可以成為componentDidUnmount
。
代碼的執行方式是從上到下。 因為它是功能性的,它將運行一次並使用在 useState 的參數中設置的默認useState
進行渲染。 它不會阻止從 useEffect function 中的useEffect
獲取數據。 因此,您需要能夠在沒有數據的情況下處理加載。 任何時候props.apiConfig
或props.id
發生變化,組件都會重新渲染,所有的useEffect
都會重新渲染。 如果props.apiConfig
和props.id
在第一次運行后發生變化,它只會調用useEffect
。 唯一令人討厭的部分是useEffect
不能是async
function,因此您必須在不使用await
的情況下調用 function getDataWrapper
。 當 API 接收到數據時,它將數據存儲在state
中,這將觸發組件的重新渲染。
總結:
useEffect
,它調用getDataWrapper
useState
中返回具有初始值的組件useEffect
/ getDataWrapper
function 中接收到數據,通過setState
將isLoading
設置為false
setState
現在包含的更新值重新渲染組件
useEffect
控制路徑,因為useEffect
的第二個參數中的值沒有改變。 (例如: props.apiConfig
和props.id
)。import React, { useState, useEffect } from 'react';
import { getDataFromAPI } from './api';
const MyComponent = (props) => {
const [state, useState] = useState({
isLoading: true,
data: {}
});
useEffect(() => {
const getDataWrapper = async () => {
const response = await getDataFromAPI(apiConfig, props.id);
setState({
isLoading: false,
data: response
});
});
getDataWrapper();
}, [props.apiConfig, props.id]);
if(state.isLoading) { return <div>Data is loading from API...</div>
return (
<div>
<h1>Hello, World!</h1>
<pre>{JSON.stringify(state.data, null, 2)}</pre>
</div>
);
};
export default MyComponent;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.