Javascript 承诺解析时如何重新呈现React组件?|如何在加载数据之前阻止渲染?

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 ui/Filter的功能组件。我正在使用返回异步响应的
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
  • API在
    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;