Optimistic React Apollo ui lag with React Beautiful Drag and Drop
Posted
技术标签:
【中文标题】Optimistic React Apollo ui lag with React Beautiful Drag and Drop【英文标题】: 【发布时间】:2019-02-20 22:21:20 【问题描述】:我正在尝试创建一个乐观的响应,其中 UI 立即更新(最小的延迟和更好的用户体验)拖放数据。然而,我遇到的问题是它仍然滞后。
所以发生的事情是,我希望从查询中获得区域和未分配区域的列表,unassignedZone 是一个对象,其中包含城市,而区域是其中包含城市的区域列表。在编写我的突变时,我在拖放后返回新的重新排序的区域列表。重新排序由名为“DisplayOrder”的区域对象上的字段完成。逻辑将数字设置正确。问题是当我尝试用乐观的 ui 和更新来模仿它时,会有一点延迟,就像它仍在等待网络一样。
我想要实现的大部分内容都发生在 onDragEnd = () => ... 函数中。
import React, Component from "react";
import graphql, compose, withApollo from "react-apollo";
import gql from "graphql-tag";
import withState from "recompose";
import withStyles from "@material-ui/core/styles";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import Input from "@material-ui/core/Input";
import Grid from "@material-ui/core/Grid";
import InputLabel from "@material-ui/core/InputLabel";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import AppBar from "@material-ui/core/AppBar";
import _ from "lodash";
import FormControl from "@material-ui/core/FormControl";
import move from "lodash-move";
import Zone from "../../Components/Zone";
const style =
ddlRight:
left: "3px",
position: "relative",
paddingRight: "10px"
,
ddlDrop:
marginBottom: "20px"
,
dropdownInput:
minWidth: "190px"
;
class Zones extends Component
constructor(props)
super(props);
this.state =
companyId: "",
districtId: "",
selectedTab: "Zones",
autoFocusDataId: null,
zones: [],
unassignedZone: null
;
handleChange = event =>
const client = this.props;
this.setState( [event.target.name]: event.target.value );
;
handleTabChange = (event, selectedTab) =>
this.setState( selectedTab );
;
onDragStart = () =>
this.setState(
autoFocusDataId: null
);
;
calculateLatestDisplayOrder = () =>
const allZones = this.state;
if (allZones.length === 0)
return 10;
return allZones[allZones.length - 1].displayOrder + 10;
;
updateCitiesDisplayOrder = cities =>
let displayOrder = 0;
const reorderedCities = _.map(cities, aCity =>
displayOrder += 10;
const city = ...aCity, ... displayOrder ;
if (city.ZonesCities)
city.ZonesCities.displayOrder = displayOrder;
return city;
);
return reorderedCities;
;
moveAndUpdateDisplayOrder = (allZones, result) =>
const reorderedZones = _.cloneDeep(
move(allZones, result.source.index, result.destination.index)
);
let displayOrder = 0;
_.each(reorderedZones, (aZone, index) =>
displayOrder += 10;
aZone.displayOrder = displayOrder;
);
return reorderedZones;
;
/**
* droppable id board represents zones
* @param result [holds our source and destination draggable content]
* @return
*/
onDragEnd = result =>
console.log("Dragging");
if (!result.destination)
return;
const source = result.source;
const destination = result.destination;
if (
source.droppableId === destination.droppableId &&
source.index === destination.index
)
return;
const
zonesByCompanyAndDistrict,
unassignedZoneByCompanyAndDistrict
= this.props.zones;
// reordering column
if (result.type === "COLUMN")
if (result.source.index < 0 || result.destination.index < 0)
return;
const reorderZones, companyId, districtId = this.props;
const sourceData = zonesByCompanyAndDistrict[result.source.index];
const destinationData =
zonesByCompanyAndDistrict[result.destination.index];
const reorderedZones = this.moveAndUpdateDisplayOrder(
zonesByCompanyAndDistrict,
result
);
console.log(reorderedZones);
console.log(unassignedZoneByCompanyAndDistrict);
reorderZones(
variables:
companyId,
districtId,
sourceDisplayOrder: sourceData.displayOrder,
destinationDisplayOrder: destinationData.displayOrder,
zoneId: sourceData.id
,
optimisticResponse:
__typename: "Mutation",
reorderZones:
zonesByCompanyAndDistrict: reorderedZones
,
// refetchQueries: () => ["zones"],
update: (store, data: reorderZones ) =>
const data = store.readQuery(
query: unassignedAndZonesQuery,
variables:
companyId,
districtId
);
store.writeQuery(
query: unassignedAndZonesQuery,
data: data
);
);
// this.setState( zones: reorderedZones );
// Need to reorder zones api call here
// TODO: Elixir endpoint to reorder zones
return;
;
render()
const selectedTab = this.state;
const
classes,
companies,
districts,
companyId,
districtId,
setCompanyId,
setDistrictId,
zones
= this.props;
const isDisabled = !companyId || !districtId;
return (
<Grid container spacing=16>
<Grid container spacing=16 className=classes.ddlDrop>
<Grid item xs=12 className=classes.ddlRight>
<h2>Company Zones</h2>
</Grid>
<Grid item xs=2 className=classes.ddlRight>
<FormControl>
<InputLabel htmlFor="company-helper">Company</InputLabel>
<Select
value=companyId
onChange=event =>
setCompanyId(event.target.value);
input=
<Input
name="companyId"
id="company-helper"
className=classes.dropdownInput
/>
>
_.map(companies.companies, aCompany =>
return (
<MenuItem
value=aCompany.id
key=`companyItem-$aCompany.id`
>
aCompany.name
</MenuItem>
);
)
</Select>
</FormControl>
</Grid>
<Grid item xs=2 className=classes.ddlRight>
<FormControl>
<InputLabel htmlFor="district-helper">District</InputLabel>
<Select
value=districtId
onChange=event =>
setDistrictId(event.target.value);
input=
<Input
name="districtId"
id="district-helper"
className=classes.dropdownInput
/>
>
_.map(districts.districts, aDistrict =>
return (
<MenuItem
value=aDistrict.id
key=`districtItem-$aDistrict.id`
>
aDistrict.name
</MenuItem>
);
)
</Select>
</FormControl>
</Grid>
</Grid>
<Grid container>
<AppBar position="static" color="primary">
<Tabs value=selectedTab onChange=this.handleTabChange>
<Tab value="Zones" label="Zone" disabled=isDisabled />
<Tab
value="Pricing Structure"
label="Pricing Structure"
disabled=isDisabled
/>
<Tab value="Pricing" label="Pricing" disabled=isDisabled />
<Tab
value="Student Pricing"
label="Student Pricing"
disabled=isDisabled
/>
</Tabs>
</AppBar>
selectedTab === "Zones" &&
zones &&
zones.zonesByCompanyAndDistrict && (
<Zone
onDragStart=this.onDragStart
onDragEnd=this.onDragEnd
zones=_.sortBy(zones.zonesByCompanyAndDistrict, [
"displayOrder"
])
unassignedZone=zones.unassignedZoneByCompanyAndDistrict
/>
)
selectedTab === "Pricing Structure" && <div>Pricing Structure</div>
selectedTab === "Pricing" && <div>Pricing</div>
selectedTab === "Student Pricing" && <div>Student Pricing</div>
</Grid>
</Grid>
);
const companiesQuery = gql`
query allCompanies
companies
id
name
`;
const districtsQuery = gql`
query allDistricts
districts
id
name
`;
const unassignedAndZonesQuery = gql`
query zones($companyId: String!, $districtId: String!)
unassignedZoneByCompanyAndDistrict(
companyId: $companyId
districtId: $districtId
)
name
description
displayOrder
cities
id
name
zonesByCompanyAndDistrict(companyId: $companyId, districtId: $districtId)
id
name
description
displayOrder
basePrice
zoneCities
displayOrder
city
id
name
`;
const reorderZones = gql`
mutation(
$companyId: String!
$districtId: String!
$sourceDisplayOrder: Int!
$destinationDisplayOrder: Int!
$zoneId: String!
)
reorderZones(
companyId: $companyId
districtId: $districtId
sourceDisplayOrder: $sourceDisplayOrder
destinationDisplayOrder: $destinationDisplayOrder
zoneId: $zoneId
)
id
__typename
name
description
displayOrder
basePrice
zoneCities
displayOrder
city
id
name
`;
export default compose(
withState("companyId", "setCompanyId", ""),
withState("districtId", "setDistrictId", ""),
graphql(unassignedAndZonesQuery,
name: "zones",
skip: ( companyId, districtId ) => !(companyId && districtId),
options: ( companyId, districtId ) => (
variables: companyId, districtId ,
fetchPolicy: "cache-and-network"
)
),
graphql(companiesQuery,
name: "companies",
options: fetchPolicy: "cache-and-network"
),
graphql(districtsQuery,
name: "districts",
options: fetchPolicy: "cache-and-network"
),
graphql(reorderZones,
name: "reorderZones"
),
withApollo,
withStyles(style)
)(Zones);
https://drive.google.com/file/d/1ujxTOGr0YopeBxrGfKDGfd1Cl0HiMaK0/view?usp=sharing
【问题讨论】:
【参考方案1】:对于遇到同样问题的任何人,主要问题是我的更新/乐观响应都不正确。这里要提的是这个块:
update: (store, data: reorderZones ) =>
const
zonesByCompanyAndDistrict,
unassignedZoneByCompanyAndDistrict
= store.readQuery(
query: unassignedAndZonesQuery,
variables:
companyId,
districtId
);
const reorderedZones = this.moveAndUpdateDisplayOrder(
zonesByCompanyAndDistrict,
result
);
store.writeQuery(
query: unassignedAndZonesQuery,
variables: companyId, districtId ,
data:
unassignedZoneByCompanyAndDistrict,
zonesByCompanyAndDistrict: reorderedZones
);
如果您将它与我的原始代码进行比较,您会发现,当我 writeQuery 时,这次我有变量。查看 apollo devtools,我看到添加了一个条目,只是一个带有错误变量的条目。所以这很容易解决。乐观的响应是正确的(模仿从我们的突变返回的有效负载)。错误的另一个方面是,我获取所有这些数据的查询最初有一个缓存和网络的获取策略,这意味着当我们接收到数据时我们缓存它,并且我们要求最新的数据。所以这将始终获取最新数据。我不需要那个,因此会有一点滞后,我只需要乐观响应。默认情况下,apollo 会先缓存,它会在缓存中查找数据,如果不存在,我们会通过网络获取它。与缓存更新和慢速网络完美搭配。
【讨论】:
以上是关于Optimistic React Apollo ui lag with React Beautiful Drag and Drop的主要内容,如果未能解决你的问题,请参考以下文章
Reactjs/Apollo/AppSync Mutation Optimistic Response Resolved ID
React Apollo 显示“react-apollo 仅支持每个 HOC 的查询、订阅或突变”错误
Prisma Playground 中的 React + Apollo“入门”错误