处理 ag grid react 并渲染复选框网格
Posted
技术标签:
【中文标题】处理 ag grid react 并渲染复选框网格【英文标题】:Dealing with ag grid react and rendering a grid of checkboxes 【发布时间】:2018-12-31 14:44:07 【问题描述】:我正在搞乱 ag-grid、react-apollo,而且一切似乎都运行良好。这里的目标是单击一个复选框并发生修改某些数据的突变/网络请求。我遇到的问题是它重绘了整个行,这可能真的很慢,但我真的只是想更新单元格本身,所以它很快并且用户体验更好。我的一个想法是进行乐观更新,然后更新我的缓存/利用我的缓存。你们采取了哪些方法。
列和行数据都是通过 apollo 查询获取的。
这里有一些代码:
复选框渲染器
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.log(data);
// console.log(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.log(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);
【问题讨论】:
初始渲染后,是否需要从外部事件同步网格状态,即有人从另一个地方更新这些值或从后端到达新值? 【参考方案1】:我不知道 ag-grid 但是...在这种情况下发出请求会导致整个网格(UserRole 组件)重绘。
当您传递影响整个父状态的操作(给子子)时,这是正常的(新数据到达道具 => 重绘)。
您可以通过 shouldComponentUpdate() 避免这种情况 - f.e.仅当行数量发生变化时才重绘。
但还有另一个问题 - 您正在做出乐观的更改(更改复选框状态) - 如果突变失败怎么办?您必须处理阿波罗错误并强制重绘整个网格 - 更改是本地的(单元格)。这可以做到通过在 shouldComponentUpdate 中设置标志(使用 setState)和附加条件。
【讨论】:
这是我最终要走的路线,我在错误时强制重新渲染,因为我想要准确性。【参考方案2】:对我来说处理这个问题的最好方法是在 apollo 中使用网络状态进行 shouldComponentUpdate,这需要进行一些挖掘才能看到发生了什么:
/**
* 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
这部分代码只是说我希望初始加载只导致 UI 更新。我相信它可以从加载 -> 成功作为网络状态,然后如果你从成功重新获取 -> 重新获取 -> 成功或类似性质的东西。
基本上,我只是查看了查询的道具,看看我可以使用什么。
【讨论】:
以上是关于处理 ag grid react 并渲染复选框网格的主要内容,如果未能解决你的问题,请参考以下文章