我正在尝试创建一个乐观的响应,其中 ui 会立即通过拖放数据进行更新(最小的延迟和更好的用户体验)。然而,我遇到的问题是它仍然滞后。
所以发生的事情是,我希望从我的查询中得到一个区域和未分配区域的列表, unassignedZone 是一个对象,其中包含城市,而 zone 是其中包含城市的区域列表。在编写我的突变时,我在拖放后返回新的重新排序的区域列表。重新排序由名为“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 <- 这是一个演示它发生的视频。