简体   繁体   中英

How to refresh Fabric-Ui DetailsList when updating a row

I'm trying to create a fabric-ui detailsList component. This component should simply represents what i have in my database with the possibility to update a specific column value for each row. To do the updating, each row should have a fabric-ui PrimaryButton. I also created two APIs in my server (GET and POST). The GET request will returns to the react app all the resources that will be shown and the POST (called whne clicking on the PrimaryButton of a specific row) will have in parameter the Id of the row and will update the value of the column.

I created a root component:App which load DetailsList and call GET API to get all the resources and show them. I also created a child component:ResolveButton which will be called for each row in the details list in the root component.

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;

As you can see in my App.tsx i create the ResolveButton component when the column key is "Id". My problem is that when clicking on the button the data will be updated in the database but what is shown in the react app is always the old version of the database, so i need to refresh the page when calling the POST API.

This is a react type question.

Your DetailList uses sortedItems to manage its state. Therefore, when you click on ResolveButton you need to update the sate. Which is not happening

To fix this, ResolveButton should expose a property called onResolved so the main component could handle to update its state.

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);
     }
  }
}

.

In the App just call the onResolved to update the state

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM