1
votes

Comment continuer à recevoir des données tout en gardant les données en ordre?

Je travaille sur ce tri de table de réaction lorsque l'utilisateur clique sur l'en-tête de la table dont il a besoin pour trier la table, le tri fonctionne mais le problème est que je reçois de nouvelles données toutes les secondes via le hub SignalR et qu'il définit l'état udata aux nouvelles données. Lorsqu'un utilisateur clique sur l'en-tête du tableau, il trie le tableau mais revient à nouveau au nouvel état modifié par de nouvelles données. Et annule la table triée comme non triée.

Est-il possible de conserver l'état trié tout en continuant de recevoir des données?

Je suis nouveau pour réagir, toute aide serait appréciée

constructor() {
    super()
    this.state = {
      udata: [],
      sort: {
        column: null,
        direction: 'desc',
      },
    }
  }

  componentDidMount() {
    let connection = new signalR.HubConnectionBuilder()
      .withUrl('/signalserver')
      .build()

    connection
      .start()
      .then(function() {})
      .catch(function(err) {
        return console.error(err.toString())
      })
    connection.on(
      'APIChannel',
      function(data) {
        this.setState({udata: data})
      }.bind(this),
    )

    async function start() {
      try {
        await connection.start()
        console.log('connected')
      } catch (err) {
        console.log(err)
        setTimeout(() => start(), 5000)
      }
    }

    connection.onclose(async () => {
      await start()
    })
  }

  onSort(column) {
    return function(e) {
      let direction = this.state.sort.direction

      if (this.state.sort.column === column) {
        // Change the sort direction if the same column is sorted.
        direction = this.state.sort.direction === 'asc' ? 'desc' : 'asc'
      }

      // Sort ascending.
      const sortedData = this.state.udata.sort((a, b) => {
        if (column === 'appName') {
          // This sorts strings taking into consideration numbers in strings.
          // e.g., Account 1, Account 2, Account 10. Normal sorting would sort it Account 1, Account 10, Account 2.
          const collator = new Intl.Collator(undefined, {
            numeric: true,
            sensitivity: 'base',
          })

          return collator.compare(a.appName, b.appName)
        } else {
          return a.contractValue - b.contractValue
        }
      })

      // Reverse the order if direction is descending.
      if (direction === 'desc') {
        sortedData.reverse()
      }

      // Set the new state.
      this.setState({
        udata: sortedData,
        sort: {
          column,
          direction,
        },
      })
    }.bind(this) // Bind "this" again because the onSort function is returning another function.
  }

  renderItem(item, key) {
    const itemRows = [
      <tr onClick={clickCallback} key={'row-data-' + key}>
        <td>{item.appName}</td>
        <td>
          <h6 className="text-muted">
            <i
              className={
                'fa fa-circle text-c-' +
                (item.appState === 'STARTED' ? 'green' : 'red') +
                ' f-10 m-r-15'
              }
            />
            {item.appState}
          </h6>
        </td>
        <td>{item.spaceName}</td>
        <td>
          <h6 className="text-muted">{item.orgName}</h6>
        </td>
        <td>
          <h6 className="text-muted">
            {new Date(item.appUpdatedAt).toLocaleString()}
          </h6>
        </td>
      </tr>,
    ]

    return itemRows
  }

  render() {
    let allItemRows = []

    this.state.udata.forEach((item, key) => {
      const perItemRows = this.renderItem(item, key)
      allItemRows = allItemRows.concat(perItemRows)
    })

    return (
      <Aux>
        <Row>
          <Table hover responsive>
            <thead>
              <tr>
                <th className="sortable" onClick={this.onSort('appName')}>
                  {' '}
                  Account Name
                </th>
                <th> State</th>
                <th> Space</th>
                <th> Organization</th>
                <th className="sortable" onClick={this.onSort('appUpdatedAt')}>
                  {' '}
                  Updated At
                </th>
              </tr>
            </thead>
            <tbody> {allItemRows}</tbody>
          </Table>
        </Row>
      </Aux>
    )
  }


2 commentaires

vous devez enregistrer la sélection de tri dans l'état et en fonction du type de tri, vous devez trier les données dans le rendu, au lieu de définir l'état "udata: sortedData", de cette façon, lorsque vous indiquez la mise à jour de l'API, vous pouvez directement en fonction du tri rendre en les triant


Ajout d'une solution, vérifiez-la et faites-le moi savoir.


3 Réponses :


0
votes

Utilisez un composant parent pour exécuter la requête et transmettre les valeurs non triées et la valeur d'ordre de tri à un composant enfant. Le composant enfant (le composant de table le plus probable) affichera les données en fonction de la valeur de l'ordre de tri.

Actuellement, votre composant est monté chaque fois que vous modifiez les valeurs d'état


0 commentaires

0
votes

Ajoutez une méthode de cycle de vie appelée componentWillReceiveProps (nextProps) ou vous pouvez également utiliser static getDerivedStateFromProps (props, state) pour effectuer le tri à l'intérieur de cette méthode, qui Lorsque de nouvelles données sont disponibles, elles seront automatiquement triées avec celles d'origine qui s'y trouvaient. par conséquent, tous vos autres codes restent les mêmes et les nouvelles données prennent juste leur place dans le tri.


0 commentaires

1
votes

Déplacez la partie tri de la fonction vers une nouvelle fonction:

import React, { Component } from "react";
import { Row, Col, Form, Card, Table, Tab, Nav } from "react-bootstrap";
import Aux from "../../hoc/_Aux";
import * as signalR from "@aspnet/signalr";

class Dashboard extends Component {
  constructor() {
    super();
    this.state = {
      udata: [],
      sysdata: [],
      expandedRows: [],
      user: "active",
      system: "",
      data: [],
      UserFilters: {
        appState: [],
        orgName: [],
        spaceName: []
      },
      SysFilters: {
        appState: []
      },
      intervalId: 0, //Scroll on top feature
      sort: {
        column: null,
        direction: "desc"
      }
    };
  }

  sortData = (data, column, direction) => {
    // Sort ascending.
    const sortedData = data.sort((a, b) => {
      if (column === 'appName') {
        const collator = new Intl.Collator(undefined, {
          numeric: true,
          sensitivity: 'base',
        })

        return collator.compare(a.appName, b.appName)
      } else {
        return a.contractValue - b.contractValue
      }
    })

    // Reverse the order if direction is descending.
    if (direction === 'desc') {
      return sortedData.reverse()
    }
    return sortedData
  };

  componentDidMount() {
    let connection = new signalR.HubConnectionBuilder()
      .withUrl("/signalserver")
      .build();

    connection
      .start()
      .then(function () { })
      .catch(function (err) {
        return console.error(err.toString());
      });

    connection.on(
      "SBUserBrodcasting",
      function (data) {
        let sortedData = [];
        if (this.state.sort.column) {
          sortedData = this.sortData(
            data,
            this.state.sort.column,
            this.state.sort.direction
          );
        } else {
          sortedData = data;
        }
        this.setState({ udata: sortedData });
      }.bind(this)
    );

    connection.on(
      "SBSystemBrodcasting",
      function (data) {
        this.setState({ sysdata: data });
      }.bind(this)
    );

    async function start() {
      try {
        await connection.start();
        console.log("connected");
      } catch (err) {
        console.log(err);
        setTimeout(() => start(), 5000);
      }
    }

    connection.onclose(async () => {
      await start();
    });
  }

    onSort(column) {
      return function (e) {
        let direction = this.state.sort.direction;

        if (this.state.sort.column === column) {
          // Change the sort direction if the same column is sorted.
          direction = this.state.sort.direction === "asc" ? "desc" : "asc";
        }

        // Sort ascending.
        const sortedData = this.sortData(this.state.udata, column, direction);

        // Set the new state.
        this.setState({
          udata: sortedData,
          sort: {
            column,
            direction
          }
        });
      }.bind(this); // Bind "this" again because the onSort function is returning another function.
    }

  scrollStep() {
    if (window.pageYOffset === 0) {
      clearInterval(this.state.intervalId);
    }
    window.scroll(0, window.pageYOffset - this.props.scrollStepInPx);
  }

  scrollToTop() {
    let intervalId = setInterval(
      this.scrollStep.bind(this),
      this.props.delayInMs
    );
    this.setState({ intervalId: intervalId });
  }

  FilterUserArray = (array, UserFilters) => {
    let getValue = value =>
      typeof value === "string" ? value.toUpperCase() : value;

    const filterKeys = Object.keys(UserFilters);
    return array.filter(item => {
      // validates all filter criteria
      return filterKeys.every(key => {
        // ignores an empty filter
        if (!UserFilters[key].length) return true;
        return UserFilters[key].find(
          filter => getValue(filter) === getValue(item[key])
        );
      });
    });
  };

  FilterSysArray = (array, SysFilters) => {
    let getValue = value =>
      typeof value === "string" ? value.toUpperCase() : value;

    const filterKeys = Object.keys(SysFilters);
    return array.filter(item => {
      // validates all filter criteria
      return filterKeys.every(key => {
        // ignores an empty filter
        if (!SysFilters[key].length) return true;
        return SysFilters[key].find(
          filter => getValue(filter) === getValue(item[key])
        );
      });
    });
  };

  HandleRowClick(rowId) {
    const currentExpandedRows = this.state.expandedRows;
    const isRowCurrentlyExpanded = currentExpandedRows.includes(rowId);
    const newExpandedRows = isRowCurrentlyExpanded
      ? currentExpandedRows.filter(id => id !== rowId)
      : currentExpandedRows.concat(rowId);
    this.setState({ expandedRows: newExpandedRows });
  }

  SpaceRenderFilterList(item, key) {
    const itemRows = [
      <li key={"li-data-" + key}>
        <Form.Check
          custom
          type="checkbox"
          value={item}
          id={"SBSpace-" + item}
          label={item}
          onChange={this.UserAppSpaceFilter.bind(this)}
        />
      </li>
    ];
    return itemRows;
  }

  OrgRenderFilterList(item, key) {
    const itemRows = [
      <li key={"li-data-" + key}>
        <Form.Check
          custom
          type="checkbox"
          value={item}
          id={"SBOrg-" + item}
          label={item}
          onChange={this.UserAppOrgFilter.bind(this)}
        />
      </li>
    ];
    return itemRows;
  }

  RenderItem(item, key) {
    const clickCallback = () => this.HandleRowClick(key);
    const itemRows = [
      <tr onClick={clickCallback} key={"row-data-" + key}>
        <td>{item.appName}</td>
        <td>
          <h6 className="text-muted">
            <i
              className={
                "fa fa-circle text-c-" +
                (item.appState === "STARTED" ? "green" : "red") +
                " f-10 m-r-15"
              }
            />
            {item.appState}
          </h6>
        </td>
        <td>{item.spaceName}</td>
        <td>
          <h6 className="text-muted">{item.orgName}</h6>
        </td>
        <td>
          <h6 className="text-muted">
            {new Date(item.appUpdatedAt).toLocaleString()}
          </h6>
        </td>
      </tr>
    ];

    if (this.state.expandedRows.includes(key)) {
      itemRows.push(
        <tr key={"row-expanded-" + key}>
          <td colSpan="6">
            <Card className="card-event">
              <Card.Body>
                <div className="row align-items-center justify-content-center">
                  <div className="col">
                    <h5 className="m-0">Upcoming Event</h5>
                  </div>
                  <div className="col-auto">
                    <label className="label theme-bg2 text-white f-14 f-w-400 float-right">
                      34%
                    </label>
                  </div>
                </div>
                <h2 className="mt-2 f-w-300">
                  45<sub className="text-muted f-14">Competitors</sub>
                </h2>
                <h6 className="text-muted mt-3 mb-0">
                  You can participate in event{" "}
                </h6>
                <i className="fa fa-angellist text-c-purple f-50" />
              </Card.Body>
            </Card>
          </td>
        </tr>
      );
    }

    return itemRows;
  }

  onClickfn = () => {
    this.setState({ user: "active", system: "inactive" });
  };

  onClickfnsys = () => {
    this.setState({ user: "inactive", system: "active" });
  };

  UserAppStateFilter(e) {
    let index;
    // current array of options
    const options = this.state.UserFilters.appState;
    // check if the check box is checked or unchecked
    if (e.target.checked) {
      // add the numerical value of the checkbox to options array
      options.push(e.target.value);
    } else {
      // or remove the value from the unchecked checkbox from the array
      index = options.indexOf(e.target.value);
      options.splice(index, 1);
    }
    // update the state with the new array of options
    this.setState({
      UserFilters: { ...this.state.UserFilters, appState: options }
    });
  }

  UserAppSpaceFilter(e) {
    let index;
    // current array of options
    const options = this.state.UserFilters.spaceName;
    // check if the check box is checked or unchecked
    if (e.target.checked) {
      // add the numerical value of the checkbox to options array
      options.push(e.target.value);
    } else {
      // or remove the value from the unchecked checkbox from the array
      index = options.indexOf(e.target.value);
      options.splice(index, 1);
    }
    // update the state with the new array of options
    this.setState({
      UserFilters: { ...this.state.UserFilters, spaceName: options }
    });
  }

  UserAppOrgFilter(e) {
    let index;
    // current array of options
    const options = this.state.UserFilters.orgName;
    // check if the check box is checked or unchecked
    if (e.target.checked) {
      // add the numerical value of the checkbox to options array
      options.push(e.target.value);
    } else {
      // or remove the value from the unchecked checkbox from the array
      index = options.indexOf(e.target.value);
      options.splice(index, 1);
    }
    // update the state with the new array of options
    this.setState({
      UserFilters: { ...this.state.UserFilters, orgName: options }
    });
  }

  SysAppStateFilter(e) {
    let index;
    // current array of options
    const options = this.state.SysFilters.appState;
    // check if the check box is checked or unchecked
    if (e.target.checked) {
      // add the numerical value of the checkbox to options array
      options.push(e.target.value);
    } else {
      // or remove the value from the unchecked checkbox from the array
      index = options.indexOf(e.target.value);
      options.splice(index, 1);
    }
    // update the state with the new array of options
    this.setState({
      SysFilters: { ...this.state.SysFilters, appState: options }
    });
  }

  render() {
    let Spacefilterlist = [];

    Array.from(new Set(this.state.udata.map(item => item.spaceName))).forEach(
      (item, key) => {
        const perItemRows = this.SpaceRenderFilterList(item, key);
        Spacefilterlist = Spacefilterlist.concat(perItemRows);
      }
    );

    let Orgfilterlist = [];

    Array.from(new Set(this.state.udata.map(item => item.orgName))).forEach(
      (item, key) => {
        const perItemRows = this.OrgRenderFilterList(item, key);
        Orgfilterlist = Orgfilterlist.concat(perItemRows);
      }
    );

    let allItemRows = [];

    this.FilterUserArray(this.state.udata, this.state.UserFilters).forEach(
      (item, key) => {
        const perItemRows = this.RenderItem(item, key);
        allItemRows = allItemRows.concat(perItemRows);
      }
    );

    let sysallItemRows = [];

    this.FilterSysArray(this.state.sysdata, this.state.SysFilters).forEach(
      (item, key) => {
        const perItemRows = this.RenderItem(item, key);
        sysallItemRows = sysallItemRows.concat(perItemRows);
      }
    );

    return (
      <Aux>
        <Row>
          <Col sm={12}>
            <Tab.Container defaultActiveKey="user">
              <Row>
                <Col sm={2}>
                  <Nav variant="pills" className="flex-column">
                    <Nav.Item>
                      <Nav.Link eventKey="user" onClick={this.onClickfn}>
                        User
                      </Nav.Link>
                    </Nav.Item>
                    <Nav.Item>
                      <Nav.Link eventKey="system" onClick={this.onClickfnsys}>
                        System
                      </Nav.Link>
                    </Nav.Item>
                  </Nav>
                  <br />
                  <Card
                    style={{
                      display: this.state.user === "active" ? "" : "none"
                    }}
                  >
                    <Tab.Pane eventKey="user">
                      <Card.Header>
                        <Card.Title as="h5">Filters</Card.Title>
                      </Card.Header>
                      <Card.Body>
                        <h6>By State</h6>
                        <hr />
                        <ul className="list-inline m-b-0">
                          <Form.Group onReset={this.handleFormReset}>
                            <li>
                              <Form.Check
                                custom
                                type="checkbox"
                                id="checkbox1"
                                value="STARTED"
                                label="STARTED"
                                onChange={this.UserAppStateFilter.bind(this)}
                              />
                            </li>
                            <li>
                              <Form.Check
                                custom
                                type="checkbox"
                                id="checkbox2"
                                value="STOPPED"
                                label="STOPPED"
                                onChange={this.UserAppStateFilter.bind(this)}
                              />
                            </li>
                          </Form.Group>
                        </ul>
                        <h6>By Space</h6>
                        <hr />
                        <ul className="list-inline m-b-0">
                          <Form.Group>{Spacefilterlist}</Form.Group>
                        </ul>
                        <h6>By Organization</h6>
                        <hr />
                        <ul className="list-inline m-b-0">
                          <Form.Group>{Orgfilterlist}</Form.Group>
                        </ul>
                      </Card.Body>
                    </Tab.Pane>
                  </Card>
                  <Card>
                    <Tab.Pane
                      eventKey="system"
                      style={{
                        display: this.state.system === "active" ? "" : "none"
                      }}
                    >
                      <Card.Header>
                        <Card.Title as="h5">Filters</Card.Title>
                      </Card.Header>
                      <Card.Body>
                        <h6>By State</h6>
                        <hr />
                        <ul className="list-inline m-b-0">
                          <Form.Group>
                            <li>
                              <Form.Check
                                custom
                                type="checkbox"
                                id="chec1"
                                value="STARTED"
                                label="STARTED"
                                onChange={this.SysAppStateFilter.bind(this)}
                              />
                            </li>
                            <li>
                              <Form.Check
                                custom
                                type="checkbox"
                                id="chec2"
                                value="STOPPED"
                                label="STOPPED"
                                onChange={this.SysAppStateFilter.bind(this)}
                              />
                            </li>
                          </Form.Group>
                        </ul>
                      </Card.Body>
                    </Tab.Pane>
                  </Card>
                </Col>
                <Col sm={10}>
                  <Tab.Content>
                    <Tab.Pane eventKey="user">
                      <Table hover responsive>
                        <thead>
                          <tr>
                            <th
                              className="sortable"
                              onClick={this.onSort("appName")}
                            >
                              Account Name
                            </th>
                            <th>State</th>
                            <th>Space</th>
                            <th>Organization</th>
                            <th
                              className="sortable"
                              onClick={this.onSort("appUpdatedAt")}
                            >
                              Updated At
                            </th>
                          </tr>
                        </thead>
                        <tbody>{allItemRows}</tbody>
                      </Table>
                    </Tab.Pane>
                    <Tab.Pane eventKey="system">
                      <Table hover responsive>
                        <thead>
                          <tr>
                            <th>App Name</th>
                            <th>State</th>
                            <th>Space</th>
                            <th>Organization</th>
                            <th>Updated At</th>
                          </tr>
                        </thead>
                        <tbody>{sysallItemRows}</tbody>
                      </Table>
                    </Tab.Pane>
                  </Tab.Content>
                </Col>
              </Row>
            </Tab.Container>
          </Col>
          <button
            id="myBtn"
            title="Back to top"
            className="scroll"
            onClick={() => {
              this.scrollToTop();
            }}
          >
            <span className="feather icon-chevron-up" />
          </button>
        </Row>
      </Aux>
    );
  }
}

export default Dashboard;

Vous pouvez utiliser cette fonction dans componentDidMount avant de définir l'état avec newData et aussi dans Fonction onSort .

componentDidMount() {
  // Code

   connection.on(
      'APIChannel',
      function(data) {
        let sortedData = []
        if (this.state.sort.column) {
         sortedData = this.sortData(data, this.state.sort.column, 
             this.state.sort.direction)
        } else {
         sortedData = data
        }

        this.setState({udata: sortedData})
      }.bind(this),
    )

   // Rest of the code
}

componentDidMount:

onSort(column) {
    return function(e) {
      let direction = this.state.sort.direction

      if (this.state.sort.column === column) {
        // Change the sort direction if the same column is sorted.
        direction = this.state.sort.direction === 'asc' ? 'desc' : 'asc'
      }

      // Sort ascending.
      const sortedData = this.sortData(this.state.udata, column, direction)

      // Set the new state.
      this.setState({
        udata: sortedData,
        sort: {
          column,
          direction,
        },
      })
    }.bind(this) // Bind "this" again because the onSort function is returning another function.
  }

EDIT: strong >

const sortData = (data, column, direction) => {
    // Sort ascending.
      const sortedData = data.sort((a, b) => {
        if (column === 'appName') {
          const collator = new Intl.Collator(undefined, {
            numeric: true,
            sensitivity: 'base',
          })

          return collator.compare(a.appName, b.appName)
        } else {
          return a.contractValue - b.contractValue
        }
      })

      // Reverse the order if direction is descending.
      if (direction === 'desc') {
        return sortedData.reverse()
      }
      return sortedData
}


6 commentaires

Je n'ai pas tout votre code. Ce doit être quelque chose avec un formatage. Ou vous pouvez l'ajouter à codesandbox et je peux le tester pour vous. C'est beaucoup plus facile


Le code que j'ai ajouté devrait fonctionner correctement. Prenez simplement soin des autres erreurs en premier, puis ajoutez ce code.


sans votre code cela fonctionne très bien, les seules erreurs que j'obtiens sont Ligne 52: 'sorterdData' n'est pas défini no-undef Ligne 68: 'sortData' n'est pas défini no-undef Ligne 105: 'sortData' est non défini no-undef


Ajoutez this. à la fonction où elle est appelée.


erreur sur 'onSort' n'est pas défini no-undef J'ai supprimé const car il donnait l'erreur onSort n'est pas une fonction


y a-t-il une raison pour laquelle vous avez ajouté deux fois la fonction sortData ?