Работа с реакцией ag grid и рендеринг сетки флажков

Я возился с ag-grid, react-apollo, и вроде все работает нормально. Цель состоит в том, чтобы установить флажок и получить запрос на мутацию / сетевой запрос, изменяющий некоторые данные. Проблема, с которой я столкнулся, заключается в том, что он перерисовывает всю строку, что может быть очень медленным, но на самом деле я просто пытаюсь обновить саму ячейку, чтобы она была быстрой и удобной для пользователя. Одна мысль, которая у меня была, заключалась в том, чтобы сделать оптимистичное обновление и просто обновить мой кеш / использовать мой кеш. Какой у вас подход, ребята?

Данные столбцов и строк захватываются с помощью запроса apollo.

Вот код:

CheckboxRenderer

import React, { Component } from "react";
import Checkbox from "@material-ui/core/Checkbox";
import _ from "lodash";

class CheckboxItem extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: false
    };
    this.handleCheckboxChange = this.handleCheckboxChange.bind(this);
  }

  componentDidMount() {
    this.setDefaultState();
  }

  setDefaultState() {
    const { data, colDef, api } = this.props;
    const { externalData } = api;
    if (externalData && externalData.length > 0) {
      if (_.find(data.roles, _.matchesProperty("name", colDef.headerName))) {
        this.setState({
          value: true
        });
      }
    }
  }

  updateGridAssociation(checked) {
    const { data, colDef } = this.props;
    // const { externalData, entitySpec, fieldSpec } = this.props.api;
    // console.info(data);
    // console.info(colDef);
    if (checked) {
      this.props.api.assign(data.id, colDef.id);
      return;
    }
    this.props.api.unassign(data.id, colDef.id);
    return;
  }

  handleCheckboxChange(event) {
    const checked = !this.state.value;
    this.updateGridAssociation(checked);
    this.setState({ value: checked });
  }

  render() {
    return (
      <Checkbox
        checked = {this.state.value}
        onChange = {this.handleCheckboxChange}
      />
    );
  }
}

export default CheckboxItem;

Сама сетка:

import React, { Component } from "react";
import { graphql, compose } from "react-apollo";
import gql from "graphql-tag";
import Grid from "@material-ui/core/Grid";
import _ from "lodash";
import { AgGridReact } from "ag-grid-react";
import { CheckboxItem } from "../Grid";
import "ag-grid/dist/styles/ag-grid.css";
import "ag-grid/dist/styles/ag-theme-material.css";

class UserRole extends Component {
  constructor(props) {
    super(props);
    this.api = null;
  }

  generateColumns = roles => {
    const columns = [];
    const initialColumn = {
      headerName: "User Email",
      editable: false,
      field: "email"
    };
    columns.push(initialColumn);
    _.forEach(roles, role => {
      const roleColumn = {
        headerName: role.name,
        editable: false,
        cellRendererFramework: CheckboxItem,
        id: role.id,
        suppressMenu: true,
        suppressSorting: true
      };
      columns.push(roleColumn);
    });
    if (this.api.setColumnDefs && roles) {
      this.api.setColumnDefs(columns);
    }
    return columns;
  };

  onGridReady = params => {
    this.api = params.api;
    this.columnApi = params.columnApi;
    this.api.assign = (userId, roleId) => {
      this.props.assignRole({
        variables: { userId, roleId },
        refetchQueries: () => ["allUserRoles", "isAuthenticated"]
      });
    };

    this.api.unassign = (userId, roleId) => {
      this.props.unassignRole({
        variables: { userId, roleId },
        refetchQueries: () => ["allUserRoles", "isAuthenticated"]
      });
    };
    params.api.sizeColumnsToFit();
  };

  onGridSizeChanged = params => {
    const gridWidth = document.getElementById("grid-wrapper").offsetWidth;
    const columnsToShow = [];
    const columnsToHide = [];
    let totalColsWidth = 0;
    const allColumns = params.columnApi.getAllColumns();
    for (let i = 0; i < allColumns.length; i++) {
      const column = allColumns[i];
      totalColsWidth += column.getMinWidth();
      if (totalColsWidth > gridWidth) {
        columnsToHide.push(column.colId);
      } else {
        columnsToShow.push(column.colId);
      }
    }
    params.columnApi.setColumnsVisible(columnsToShow, true);
    params.columnApi.setColumnsVisible(columnsToHide, false);
    params.api.sizeColumnsToFit();
  };

  onCellValueChanged = params => {};

  render() {
    console.info(this.props);
    const { users, roles } = this.props.userRoles;
    if (this.api) {
      this.api.setColumnDefs(this.generateColumns(roles));
      this.api.sizeColumnsToFit();
      this.api.externalData = roles;
      this.api.setRowData(_.cloneDeep(users));
    }
    return (
      <Grid
        item
        xs = {12}
        sm = {12}
        className = "ag-theme-material"
        style = {{
          height: "80vh",
          width: "100vh"
        }}
      >
        <AgGridReact
          onGridReady = {this.onGridReady}
          onGridSizeChanged = {this.onGridSizeChanged}
          columnDefs = {[]}
          enableSorting
          pagination
          paginationAutoPageSize
          enableFilter
          enableCellChangeFlash
          rowData = {_.cloneDeep(users)}
          deltaRowDataMode = {true}
          getRowNodeId = {data => data.id}
          onCellValueChanged = {this.onCellValueChanged}
        />
      </Grid>
    );
  }
}

const userRolesQuery = gql`
  query allUserRoles {
    users {
      id
      email
      roles {
        id
        name
      }
    }

    roles {
      id
      name
    }
  }
`;

const unassignRole = gql`
  mutation($userId: String!, $roleId: String!) {
    unassignUserRole(userId: $userId, roleId: $roleId) {
      id
      email
      roles {
        id
        name
      }
    }
  }
`;

const assignRole = gql`
  mutation($userId: String!, $roleId: String!) {
    assignUserRole(userId: $userId, roleId: $roleId) {
      id
      email
      roles {
        id
        name
      }
    }
  }
`;

export default compose(
  graphql(userRolesQuery, {
    name: "userRoles",
    options: { fetchPolicy: "cache-and-network" }
  }),
  graphql(unassignRole, {
    name: "unassignRole"
  }),
  graphql(assignRole, {
    name: "assignRole"
  })
)(UserRole);

Работа с реакцией ag grid и рендеринг сетки флажков

После первоначального рендеринга вам нужно синхронизировать состояние сетки по внешним событиям, то есть кто-то обновил эти значения из другого места или новые значения поступили из бэкэнда?

amankkg 03.08.2018 01:22
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
9
1
1 591
2

Ответы 2

Я не знаю ag-grid, но ... в данном случае выполнение запросов приводит к перерисовке всей сетки (компонент UserRole).

Это нормально, когда вы передаете действия (дочерним элементам), влияющие на все родительское состояние (новые данные поступили в props => перерисовать).

Вы можете избежать этого с помощью shouldComponentUpdate () - ф.э. перерисовывать только при изменении количества строк.

Но есть еще одна проблема - вы вносите оптимистичные изменения (измените состояние флажка) - что, если мутация не удалась? Вы должны обработать ошибку apollo и принудительно перерисовать всю сетку - изменение было локальным (ячейка). Это можно сделать, например. установив флаг (используя setState) и дополнительное условие в shouldComponentUpdate.

Это путь, по которому я в итоге пошел, я принудительно выполняю повторную визуализацию при ошибке, потому что мне нужна точность.

Karan 06.08.2018 23:58

Для меня лучший способ справиться с этим - выполнить shouldComponentUpdate со статусами сети в apollo, что потребовало некоторого времени, чтобы увидеть, что происходит:

 /**
   * Important to understand that we use network statuses given to us by apollo to take over, if either are 4 (refetch) we hack around it by not updating
   * IF the statuses are also equal it indicates some sort of refetching is trying to take place
   * @param  {obj} nextProps [Next props passed into react lifecycle]
   * @return {[boolean]}           [true if should update, else its false to not]
   */

  shouldComponentUpdate = nextProps => {
    const prevNetStatus = this.props.userRoles.networkStatus;
    const netStatus = nextProps.userRoles.networkStatus;
    const error = nextProps.userRoles.networkStatus === 8;
    if (error) {
      return true;
    }
    return (
      prevNetStatus !== netStatus && prevNetStatus !== 4 && netStatus !== 4
    );
  };

В основном он говорит, что если есть ошибка, просто повторите рендеринг, чтобы быть точным (и я думаю, что это нормально, если предположить, что ошибок не будет много, но вы никогда не узнаете), тогда я проверяю, не равен ли какой-либо из сетевых статусов 4 (повторная загрузка), если они Я не хочу повторного рендеринга, позвольте мне делать то, что я хочу, не вмешиваясь на этом уровне. (Как обновление дочернего компонента).

prevNetStatus !== netStatus

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

По сути, я просто заглянул в свой реквизит в поисках запроса и увидел, с чем я могу работать.

Другие вопросы по теме