Javascript 承诺解析时如何重新呈现React组件?|如何在加载数据之前阻止渲染?
我尝试更新指向azure devops ui/Filter的功能组件。我正在使用返回异步响应的Javascript 承诺解析时如何重新呈现React组件?|如何在加载数据之前阻止渲染?,javascript,node.js,reactjs,typescript,azure,Javascript,Node.js,Reactjs,Typescript,Azure,我尝试更新指向azure devops ui/Filter的功能组件。我正在使用返回异步响应的azure devops扩展sdk,以便使用此组件: 在Azure DevOps上的工作项中 我已经分别使用this.state/componentDidMount和useState/useffect对基于类和函数的组件进行了编码。我跟着 但是,我只能重新渲染状态。当状态更新时,类或功能组件中的UI组件都不会更新 我的代码有两个版本,它们都在等待响应并成功更新状态。但是,两者都不会等待UI的呈现 一般组
azure devops扩展sdk
,以便使用此组件:在Azure DevOps上的工作项中 我已经分别使用
this.state
/componentDidMount
和useState
/useffect
对基于类和函数的组件进行了编码。我跟着
但是,我只能重新渲染状态。当状态更新时,类或功能组件中的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);
}
我还尝试将ReactDOM.render
放在浏览器事件中,但我无法这样做,因为UI扩展需要一个字段来保存数据:
使用新的react钩子,编写功能性react组件非常简单。在下面的示例中,我使用了
useState
和useffect
。在基于类的React组件中,useState
钩子与this.state/this.setState
同义。useffect
钩子类似于componentDidMount
+componentdiddupdate
。它还能够卸载组件
代码将以自上而下的方式执行。因为它是功能性的,所以它将运行一次,并使用参数设置为useState
的默认状态进行渲染。它不会阻止从useffect
函数中的API获取数据。因此,您需要能够在没有数据的情况下处理加载。任何时候props.apiConfig
或props.id
更改,组件都将重新渲染,并且所有useffect
都将重新渲染如果props.apiConfig
和props.id
在第一次运行后发生更改,它将只调用useffect
。唯一令人讨厌的部分是useffect
不能是async
函数,因此您必须在不使用wait
的情况下调用函数getDataWrapper
。当API接收到数据时,它将数据存储在状态
,这将触发组件的重新呈现
总结如下:
- 调用
,它调用useffect
getDataWrapper
- 返回初始值为
useState
useffect
/getDataWrapper
函数中接收到数据后,通过setState
将状态设置为isLoading
为false
setState
现在包含的更新值重新渲染组件
- 避免使用
控制路径,因为useffect
的第二个参数中的值没有更改。(例如:useffect
和props.apiConfig
)props.id
import React,{useState,useffect}来自“React”;
从“/api”导入{getDataFromAPI};
常量MyComponent=(道具)=>{
常量[state,useState]=useState({
孤岛加载:是的,
数据:{}
});
useffect(()=>{
const getDataWrapper=async()=>{
const response=await getDataFromAPI(apiConfig,props.id);
设定状态({
孤岛加载:false,
数据:答复
});
});
getDataWrapper();
},[props.apiConfig,props.id]);
如果(state.isLoading){返回数据正在从API加载。。。
返回(
你好,世界!
{JSON.stringify(state.data,null,2)}
);
};
导出默认MyComponent;
您所说的“但我只能重新呈现状态,不能在类或功能组件中重新呈现UI组件”是什么意思在React中,无论何时更新状态或道具,都将调用render函数。它将被重新渲染。在功能组件中,当状态或道具更新时,组件将被更新。状态来自useState
hook。我不太了解TypeScript,但我认为您希望使用useS来存储promise中的数据tate
hook。在useEffect
中获取数据,并从useState
hook中设置状态。它将自动重新渲染。亲爱的@technogeek1995:尽管我放置了Dear@technogeek1995:类似于ReactDOM。render从不在两个CA上等待异步函数workItemFormService.getFieldValue的响应es,即使是使用Lazy和Suspense,我也没有使用Lazy和Suspense的经验。但是,渲染函数是非阻塞的。它永远不会等待承诺的值。它只会使用道具或状态更改重新渲染。因此,当您获得数据时,您需要设置状态,以便组件使用新状态重新渲染并更新值n您不仅会看到onClick更新了值,还会看到promise接收到数据时更新值。如果这有助于更好地解释,我可以将其形式化为一个简单的香草JS答案?是的,亲爱的@technogeek1995Thank,我再次测试了它,但它适用于简单的HTMLs标记,如:{this.state.currentState}{this.props.pdata}但是不适用于:azure devops ui/Filter你说它不适用于azure devops ui/Filter
是什么意思?你试图通过JSX输出的变量中没有数据吗?我也尝试过使用这个.state.filterStore
但是它只在simples HTMLs标记上更新,比如不在ui:Filter中,就像在我问题的图片中一样状态是更新的,而不是UI,只是在过滤器上。您从API接收的数据是否与Azure开发人员操作过滤器的道具的格式匹配?显然,由于您在普通HTML字段中正确更新了它,所以出现了一些问题
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;
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"));
}
{
"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
}
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;