Как повторно обработать React Component, когда обещание разрешается? | Как заблокировать рендер до загрузки данных? - PullRequest
0 голосов
/ 17 апреля 2020

Я пытался обновить функциональный компонент, который указывает на azure-devops-ui/Filter. Я использую azure-devops-extension-sdk, который возвращает асинхронный c ответ, чтобы использовать этот компонент:
https://developer.microsoft.com/en-us/azure-devops/components/filter внутри WorkItem на Azure DevOps

I ' мы уже кодируем как на основе классов, так и на компоненте функций, используя this.state / componentDidMount и useState / useEffect соответственно. Я подписался на этот пост .

Тем не менее, я могу только повторно визуализировать состояние. Компонент пользовательского интерфейса ни в классе, ни в функциональном компоненте не обновляется при обновлении состояния.

Есть две мои версии кода, обе они ждут ответа и успешно обновляют состояние. Однако ни один из них не будет ожидать визуализации пользовательского интерфейса.

Общий компонент:

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;

И второй абонент с 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 (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
                        }

UI issue 1 without updated state

Я также пытался поместить ReactDOM.render в событие браузера, но я не могу этого сделать, потому что расширению пользовательского интерфейса нужно поле для сохранения данные:

UI issue 2 with refresh

1 Ответ

1 голос
/ 20 апреля 2020

Создать функциональный компонент реакции просто с помощью новых React Hooks. В приведенном ниже примере я использую useState и useEffect. Хук useState является синонимом this.state/this.setState в компоненте React на основе классов. Крюк useEffect похож на componentDidMount + componentDidUpdate. Он также может быть componentDidUnmount.

Способ выполнения кода сверху вниз. Поскольку он функционален, он будет проходить один раз и визуализироваться с состоянием по умолчанию, установленным в аргументе useState. Он не будет блокировать получение данных из API в функции useEffect. Таким образом, вы должны иметь возможность обрабатывать загрузку без данных. Каждый раз, когда props.apiConfig или props.id изменяется, компонент будет повторно визуализироваться и все useEffect снова. Он будет вызывать useEffect только если props.apiConfig и props.id изменятся после первого запуска. Единственная неприятная часть в том, что useEffect не может быть функцией async, поэтому вам нужно вызвать функция getDataWrapper без использования await. Когда данные получены API, они сохранят данные в state, что вызовет повторную визуализацию компонента.

Подведем итог:

  1. Отобразите один раз с состоянием по умолчанию
    • Вызов useEffect, который вызывает getDataWrapper
    • возврат компонента с начальными значениями в useState
  2. После получения данных с помощью API в функции useEffect / getDataWrapper установите состояние с помощью setState и установите isLoading на false
  3. Повторно визуализируйте компонент с обновленным значением, которое теперь содержит setState
    • Избегайте пути управления useEffect, так как значения во втором аргументе useEffect не изменились. (например: props.apiConfig & props.id).
<code>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)}
); }; экспорт по умолчанию MyComponent;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...