reactjstypescriptfluent-uifluentui-react

Display few items in the list TypeScript/React/FluentUI


I have the following code to display a list of items using Fluent UI DetailsList control. The response includes a total of 200 items. However, I only see 30 items in the list on running the code. What am I missing?

import React from 'react';
import { useState, useEffect } from 'react';
import './App.css';
import Job from './components/Job';

function App() {  
  const [jobs, setJobs] = useState([]);
  const [err, setError] = useState({});


  useEffect(() => {
    fetch('http://demo4666569.mockable.io/groups')
    .then(response => response.json())
    .then(res => setJobs(res))
    .catch(err => setError(err))
  }, [])

  return (
    <div className="App">
      <Job jobs={jobs} />
    </div>
  );
}

export default App;

import * as React from "react";
import { DetailsList, DetailsListLayoutMode, IColumn } from '@fluentui/react/lib/DetailsList';
import { IJob } from "../interfaces/IJob.interfaces";

export interface IDetailsListBasicExampleProps {
    jobs: IJob[];
  }

export type IDetailsListBasicExampleState = {
  items: IJob[];
}

export default class Job extends React.Component<IDetailsListBasicExampleProps, IDetailsListBasicExampleState> {

  private _allItems: IJob[];
  private _columns: IColumn[];

  constructor(props:IDetailsListBasicExampleProps) {
    super(props);

    this._allItems = props.jobs;   

    this._columns = [
      { key: 'column1', name: 'Identifier', fieldName: 'Identifier', minWidth: 100, maxWidth: 200, isResizable: true },
      { key: 'column2', name: 'ObjectType', fieldName: 'ObjectType', minWidth: 100, maxWidth: 200, isResizable: true },
      { key: 'column3', name: 'InitialOnboardingTime', fieldName: 'InitialOnboardingTime', minWidth: 100, maxWidth: 200, isResizable: true },
      { key: 'column4', name: 'LastAttemptedRunTime', fieldName: 'LastAttemptedRunTime', minWidth: 100, maxWidth: 200, isResizable: true },
      { key: 'column5', name: 'LastSuccessfulRuntime', fieldName: 'LastSuccessfulRuntime', minWidth: 100, maxWidth: 200, isResizable: true },
      { key: 'column6', name: 'EstimatedNextRuntime', fieldName: 'EstimatedNextRuntime', minWidth: 100, maxWidth: 200, isResizable: true },
      { key: 'column7', name: 'Status', fieldName: 'Status', minWidth: 100, maxWidth: 200, isResizable: true },
      { key: 'column8', name: 'ThresholdIncrease', fieldName: 'ThresholdIncrease', minWidth: 100, maxWidth: 200, isResizable: true },
      { key: 'column9', name: 'ThresholdDecrease', fieldName: 'ThresholdDecrease', minWidth: 100, maxWidth: 200, isResizable: true },
    ];

    this.state = {
      items: this._allItems
    };
  }

    public render(): JSX.Element {

      const { items } = this.state;

        return(

          <div>
            <div> {items.length}</div>
          <br />
            <DetailsList
              items={items}
              columns={this._columns}
              setKey="set"
              layoutMode={DetailsListLayoutMode.justified}              
              selectionPreservedOnEmptyClick={true}
              ariaLabelForSelectionColumn="Toggle selection"
              ariaLabelForSelectAllCheckbox="Toggle selection for all items"
              checkButtonAriaLabel="select row"              
          />
          <br />
          
            </div>
        );
    }
}

Solution

  • First of all, avoid the following anti-pattern:

    "Where the props are used to set the initial state of the Component is a general anti-pattern of React. This implies that the state of the component is tied to the props of the component. The issue with doing this is that the constructor is only ever called once in the life cycle of the component. Since the props can change many times during that lifecycle, that implication is broken." ~ Read more here and here.

    You can refactor your code and completely remove the state from the Jobs Component, and pass the jobs data directly through the props:

    class Job extends React.Component {
      constructor(props) {
        super(props);
        this._columns = [ ... ];
      }
      render() {
        return (
          <div>
            <div> {this.props.jobs.length}</div>
            <br />
            <DetailsList
              items={this.props.jobs}
              columns={this._columns}
              setKey="set"
              layoutMode={DetailsListLayoutMode.justified}
              selectionPreservedOnEmptyClick={true}
              ariaLabelForSelectionColumn="Toggle selection"
              ariaLabelForSelectAllCheckbox="Toggle selection for all items"
              checkButtonAriaLabel="select row"
            />
            <br />
          </div>
        );
      }
    }
    

    Secondly, it seems that there are a lot of open Issues on GitHub related to the DetailsList Fluent UI Component and React StrictMode.

    To make the list show all the data, comment out the <StrictMode> (or probably downgrade to another version of Fluent UI that is not susceptible to that bug. See the threads on GitHub Issues for some suggestions on that).


    Full Source Code:

    import { StrictMode } from "react";
    import { createRoot } from "react-dom/client";
    import React from "react";
    import { useState, useEffect } from "react";
    import { DetailsList } from "@fluentui/react/lib/DetailsList";
    
    class Job extends React.Component {
      constructor(props) {
        super(props);
        this._columns = [
          {
            key: "column1",
            name: "Identifier",
            fieldName: "Identifier",
            minWidth: 100,
            maxWidth: 200,
            isResizable: true
          },
          {
            key: "column2",
            name: "ObjectType",
            fieldName: "ObjectType",
            minWidth: 100,
            maxWidth: 200,
            isResizable: true
          },
          {
            key: "column3",
            name: "InitialOnboardingTime",
            fieldName: "InitialOnboardingTime",
            minWidth: 100,
            maxWidth: 200,
            isResizable: true
          },
          {
            key: "column4",
            name: "LastAttemptedRunTime",
            fieldName: "LastAttemptedRunTime",
            minWidth: 100,
            maxWidth: 200,
            isResizable: true
          },
          {
            key: "column5",
            name: "LastSuccessfulRuntime",
            fieldName: "LastSuccessfulRuntime",
            minWidth: 100,
            maxWidth: 200,
            isResizable: true
          },
          {
            key: "column6",
            name: "EstimatedNextRuntime",
            fieldName: "EstimatedNextRuntime",
            minWidth: 100,
            maxWidth: 200,
            isResizable: true
          },
          {
            key: "column7",
            name: "Status",
            fieldName: "Status",
            minWidth: 100,
            maxWidth: 200,
            isResizable: true
          },
          {
            key: "column8",
            name: "ThresholdIncrease",
            fieldName: "ThresholdIncrease",
            minWidth: 100,
            maxWidth: 200,
            isResizable: true
          },
          {
            key: "column9",
            name: "ThresholdDecrease",
            fieldName: "ThresholdDecrease",
            minWidth: 100,
            maxWidth: 200,
            isResizable: true
          }
        ];
      }
      render() {
        return (
          <div>
            <div> {this.props.jobs.length}</div>
            <br />
            <DetailsList
              items={this.props.jobs}
              columns={this._columns}
              setKey="set"
              selectionPreservedOnEmptyClick={true}
              ariaLabelForSelectionColumn="Toggle selection"
              ariaLabelForSelectAllCheckbox="Toggle selection for all items"
              checkButtonAriaLabel="select row"
            />
            <br />
          </div>
        );
      }
    }
    
    function App() {
      const [jobs, setJobs] = useState([]);
    
      useEffect(() => {
        fetch("data.json")
          .then((response) => response.json())
          .then((res) => setJobs(res));
      }, []);
    
      return (
        <div className="App">
          <Job jobs={jobs} />
        </div>
      );
    }
    
    const rootElement = document.getElementById("root");
    const root = createRoot(rootElement);
    
    root.render(
      // <StrictMode>
      <App />
      // </StrictMode>
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>