如何在更新行时刷新 Fabric-Ui DetailsList

How to refresh Fabric-Ui DetailsList when updating a row

我正在尝试创建一个 fabric-ui detailsList 组件。该组件应该简单地表示我在数据库中拥有的内容,并且可以为每一行更新特定的列值。要进行更新,每一行都应该有一个 fabric-ui PrimaryButton。 我还在我的服务器中创建了两个 API(GET 和 POST)。 GET 请求将 returns 到反应应用程序将显示的所有资源,并且 POST (在单击特定行的 PrimaryButton 时调用)将在参数中包含该行的 ID 并将更新列的值。

我创建了一个根 component:App,它加载 DetailsList 并调用 GET API 来获取所有资源并显示它们。我还创建了一个子项 component:ResolveButton,它将为根组件中详细信息列表中的每一行调用。

App.tsx:

import * as React from 'react';
import ResolveButton from './ResolveButton';


export interface IDetailsListCustomColumnsExampleState {
  sortedItems?: any[];
  columns?: IColumn[];
  hasError: boolean;
}


export class App extends React.Component<{}, IDetailsListCustomColumnsExampleState> {
  public constructor(props: {}) {
    super(props);

    this.state = {
      columns: [],
      hasError:false,
      sortedItems: []
    };
    this._renderItemColumn=this._renderItemColumn.bind(this);
    this.changeStatus = this.changeStatus.bind(this);
  }
  public componentDidCatch() {
    // Display fallback UI
    this.setState({ hasError: true });
  }
  public componentDidMount(){
    this.fetchResult()
  }
  public render() {
    const { sortedItems, columns } = this.state;
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    else{
      return (
        <DetailsList
          items={sortedItems as any[]}
          setKey="set"
          columns={columns}
          onRenderItemColumn={this._renderItemColumn}
          onColumnHeaderClick={this.onColumnClick}
          onItemInvoked={this._onItemInvoked}
          onColumnHeaderContextMenu={this._onColumnHeaderContextMenu}
          ariaLabelForSelectionColumn="Toggle selection"
          ariaLabelForSelectAllCheckbox="Toggle selection for all items"
        />
      );
    }

  }
  public changeStatus (itemId:React.ReactText){
    // TODO : call the POST API to update the status
    const { sortedItems } = this.state;
    const resolvedKey='Status';
    const idKey='Id';

    sortedItems!.map(ite => {
      if(ite[idKey] === itemId){
          ite[resolvedKey] = 3;
      }
      return ite;
    })
    this.setState({
      sortedItems
    });
  }
  private fetchResult = () =>{
    fetch('https://localhost:44329/home')
    .then((response) => response.json())
    .then(json => this.setState({ columns: _buildColumns(json),
    sortedItems: json })).catch((error) => 
    { 
      this.componentDidCatch()
    })
  }

  private onColumnClick = (event: React.MouseEvent<HTMLElement>, column: IColumn): void => {
    const { columns } = this.state;
    let { sortedItems } = this.state;
    let isSortedDescending = column.isSortedDescending;

    // If we've sorted this column, flip it.
    if (column.isSorted) {
      isSortedDescending = !isSortedDescending;
    }

    // Sort the items.
    sortedItems = sortedItems!.concat([]).sort((a, b) => {
      const firstValue = a[column.fieldName || ''];
      const secondValue = b[column.fieldName || ''];

      if (isSortedDescending) {
        return firstValue > secondValue ? -1 : 1;
      } else {
        return firstValue > secondValue ? 1 : -1;
      }
    });

    // Reset the items and columns to match the state.
    this.setState({
      columns: columns!.map(col => {
        col.isSorted = col.key === column.key;

        if (col.isSorted) {
          col.isSortedDescending = isSortedDescending;
        }

        return col;
      }),
      sortedItems      
    });
  };

  private _onColumnHeaderContextMenu(column: IColumn | undefined, ev: React.MouseEvent<HTMLElement> | undefined): void {

    alert(`column ${column!.key} contextmenu opened.`);
  }

  private _onItemInvoked(item: any, index: number | undefined): void {
    alert(`Item ${item.name} at index ${index} has been invoked.`);
  }

private _renderItemColumn(item: any, index: number, column: IColumn) {

  const fieldContent = item[column.fieldName || ''];

  const crisisColor = {
    1: 'Red',
    2: 'Orange',
    3: 'Yellow',
    4: 'Green'
  }
  const crisis = {
    1: 'Crise',
    2: 'Haute',
    3: 'Moyenne',
    4: 'Basse'
  }
  const statusColor = {
    1: 'Black',
    2: 'Black',
    3: 'Green'
  } 
  const status = {
    1: 'Ouvert',
    2: 'En cours',
    3: 'Résolu'
  }
  const resolvedKey='Status';
  const isResolved = item[resolvedKey]===3;
  switch (column.key) {
    case 'Status':
      return (
        <span data-selection-disabled={true} style={{ color: statusColor[fieldContent], height: '100%', display: 'block' }}>
          {status[fieldContent]}
        </span>
      );

    case 'Criticity':
      return (
        <span data-selection-disabled={true} style={{ color: crisisColor[fieldContent], height: '100%', display: 'block' }}>
          {crisis[fieldContent]}
        </span>
      );
    case 'Creator':
      return(
        <div>
        <img src="https://img.mobiscroll.com/demos/BMW_logo.png" width="30px" height="30px" style={{verticalAlign: 'middle', display:'inline' }}/>
        <p style={{verticalAlign: 'middle', display:'inline' , paddingLeft:'10px'}}>{fieldContent}</p>
        </div>
      ); 
    case 'AssignedTo':
      return(
        <div>
        <img src="https://img.mobiscroll.com/demos/BMW_logo.png" width="30px" height="30px" style={{verticalAlign: 'middle', display:'inline' }}/>
        <p style={{verticalAlign: 'middle', display:'inline' , paddingLeft:'10px'}}>{fieldContent}</p>
        </div>
      );

    case 'Id':
      return(
        // tslint:disable-next-line jsx-no-lambda
        <ResolveButton disabled={isResolved} uniqueId={fieldContent} changeStatus={ ()=>this.changeStatus(fieldContent)} /> 
      );

    default:
      return <span>{fieldContent}</span>;
  }
}
}
function _buildColumns(json:any[]) {
  const columns = buildColumns(json);
  return columns;
}

export default App;

ResolveButton.tsx

import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import * as React from 'react';

export interface IHandleChange {
    changeStatus: ()=>void;
    disabled:boolean;
    uniqueId:string| number;
}
export class ResolveButton extends React.Component<IHandleChange, {}> {
    constructor(props:any) {

        super(props);
    }
    public render(): JSX.Element {
        return (
            <div>
                {
                    !this.props.disabled && 
                    <PrimaryButton
                        data-automation-id="test"
                        text="Résolu"
                        onClick={this.props.changeStatus}
                        allowDisabledFocus={true}
                    />
                }
            </div>
        );
    }
}
export default ResolveButton;

正如您在我的 App.tsx 中看到的那样,当列键为 "Id" 时,我创建了 ResolveButton 组件。 我的问题是,当单击按钮时,数据将在数据库中更新,但反应应用程序中显示的始终是数据库的旧版本,因此我需要在调用 POST API。

这是一道反应题。

您的 DetailList 使用 sortedItems 来管理其状态。因此,当您点击 ResolveButton 时,您需要更新状态。这没有发生

要解决此问题,ResolveButton 应该公开一个名为 onResolved 的 属性,以便主要组件可以处理更新其状态。

class ResolveButton extends React.Component<IButtonProps, {}> {
  async handleClick() {
     const response = await fetch('https://localhost:44329/home')
     const json = await response.json();

     if (this.props.onResolved) {
         this.props.onResolved(json);
     }
  }
}

.

App中调用onResolved更新状态

class App extends React.Component<{}, IDetailsListCustomColumnsExampleState> {
  …
   <ResolveButton
        disabled={isResolved}
        uniqueId={fieldContent}
        onResolved={(data) => setState({'sortedItems': data})
   />
  …
}