根据Google Maps API结果更改表格数据集



我正在尝试实现一个过滤器,它允许我根据位置和用户地址之间的距离来过滤用户。使用useMemo将数据提供到表中,基本上如下所示:

const data = useMemo(
() =>
contacts.filter(contact => {
var shouldReturn = true;
clientFilter.map((filter, i) => {
if (filter.condition === 'max_10km') {
const originAddress = `${contact['street']} ${contact['number']}, ${contact['zip']} ${contact['city']}, ${contact['country']}`;
calculateDistance(originAddress, filter.value, function(distance) {
console.log('distance is calculated: ', distance);
if (distance > 10000) {
console.log('distance is > for', contact['name']);
shouldReturn = false;
}
});
}
});
return shouldReturn;
}),
[clientFilter]
);

这很好,在控制台中,结果会按照我的预期返回。但是,我的表不会更新。我怀疑这是因为API调用的结果是异步的,因此在结果出现之前会重新对表进行渲染

我曾尝试使用useEffect更新数据,但这会使我陷入一个不断重新渲染的循环,从而超过最大值(Maximum update depth exceeded.(。

我该怎么做?我应该尝试异步函数吗?如果是,我如何等待更新我的数据,直到所有承诺都得到解决?

11月14日编辑所以,我今天一直在进一步研究这个问题。我已经设法将过滤切换到useEffect()而不是useMemo(),所以目前,它看起来是这样的:

const [filteredContacts, setFilteredContacts] = useState(contacts);
const data = useMemo(() => filteredContacts, [filteredContacts]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
console.log('Log: In useEffect()');
if (!isLoading) {
setIsLoading(true);
(async () => {
console.log('Log: State has changed, filtering will start');
filterContacts().then(() => setIsLoading(false));
})();
}
}, [clientFilter]);
async function filterContacts() {
setFilteredContacts(
contacts.filter(contact => {
var shouldReturn = true;
clientFilter.map((filter, i) => {
if (
filter.condition === 'equal' &&
contact[filter.field] != filter.value &&
shouldReturn
) {
shouldReturn = false;
}
if (filter.condition === 'max_10km' && shouldReturn) {
const originAddress = `${contact['street']} ${contact['number']}, ${contact['zip']} ${contact['city']}, ${contact['country']}`;
calculateDistance(originAddress, filter.value, async function(
distance
) {
console.log('Log: Distance is calculated: ', distance);
if (distance > 10000) {
console.log(
'Log: Distance is further away for',
contact['title']
);
shouldReturn = false;
}
});
}
});
console.log('Log: About to return shouldReturn value');
return shouldReturn;
})
);
}

现在,这适用于我的其他过滤器,但在返回shouldReturn之后,异步距离计算仍然运行。所以我的日志是这样的(我目前有16个联系人/用户(:

  • 日志:正在使用Effect((
  • 日志:状态已更改,将启动筛选
  • (16( 日志:即将返回should返回值
  • 日志:计算距离:1324
  • 日志:计算距离:4326

所以基本上,它仍然忽略了我的函数calculateDistance的异步状态。有什么想法吗?

EDIT 15/11可能也有用,这是我的calculateDistance()函数:

function calculateDistance(origin, destination, callback) {
const google = window.google;
const directionsService = new google.maps.DirectionsService();
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
callback(result.routes[0].legs[0].distance.value);
} else {
console.error('Google Maps API error: ', status);
callback(null);
}
}
);
}

编辑18/11在@Secret的建议下,我将代码更改为:

const shouldRemoveContact = await(filters, contact) = () => {
for (const filter of filters) {
if (
filter.condition === 'equal' &&
contact[filter.field] != filter.value
) {
return true
}
if (filter.condition === 'max_10km') {
const originAddress = `${contact['street']} ${contact['number']}, ${contact['zip']} ${contact['city']}, ${contact['country']}`
// please update calculateDistance to return a promise
const distance = await calculateDistance(originAddress, filter.value)
return distance > 10000
}
return false
}
}


async function filterContacts (filters, contact) {
// for every contact, run them to shouldRemoveContact
// since shouldRemoveContact is async, we use Promise.all
// to wait for the array of removeables to be ready
const shouldRemove = await Promise.all(
contact.map(c => shouldRemoveContact(filters, c))
)
// use shouldRemove to check if contact should be removed
// and voila!
return contacts.filter((c, i) => !shouldRemove[i])
}

这导致:

Syntax error: Unexpected reserved word 'await'.

在线:

const shouldRemoveContact = await(filters, contact) = () => {

您的错误是正确的-您的calculateDistance是异步的,因此您无法正确过滤数据。从本质上讲,您的代码可以归结为:

setFilteredContacts(
contacts.filter(contact => {
var shouldReturn = true;
// THIS IS WHERE CALCULATE IS
// but it doesn't do anything because it is async
// so effectively, it does nothing like a comment
console.log('Log: About to return shouldReturn value');
return shouldReturn;
})
);

正如你所看到的,你的联系人永远不会被过滤掉,因为shouldReturn总是返回true——它在该函数的中间永远不会改变!

您可以做的是预先calculate可过滤项的列表,然后使用它来运行您的过滤器。类似这样的东西(伪码(:

// given a contact and a list of filters
// asynchronously return if it should or should not be filtered
const shouldRemoveContact = async () => {}
// THEN, in the filterContacts part:
// let's generate an array of calls to shouldRemoveContact
// i.e. [true, true, false, true], where `true` means it should be removed:
// note that we use await Promise.all here, waiting for all the data to be finished.
const shouldRemove = await Promise.all(
contact.map(c => shouldRemoveContact(filters, c))
)

// we then simply shouldRemove to filter it all out
return contacts.filter((c, i) => !shouldRemove[i])

总之:

// given a list of filters and one contact
// asynchronously returns true or false depending on wheter or not
// the contact should be removed
const shouldRemoveContact = async (filters, contact) => {
for (const filter of filters) {
if (
filter.condition === 'equal' &&
contact[filter.field] != filter.value
) {
return true
}
if (filter.condition === 'max_10km') {
const originAddress = `${contact['street']} ${contact['number']}, ${contact['zip']} ${contact['city']}, ${contact['country']}`
// please update calculateDistance to return a promise
const distance = await calculateDistance(originAddress, filter.value)
return distance > 10000
, async function(
distance
) {
console.log('Log: Distance is calculated: ', distance);
if (distance > 10000) {
console.log(
'Log: Distance is further away for',
contact['title']
);
shouldReturn = false;
}
});
}
}
return false
}

async function filterContacts (filters, contact) {
// for every contact, run them to shouldRemoveContact
// since shouldRemoveContact is async, we use Promise.all
// to wait for the array of removeables to be ready
const shouldRemove = await Promise.all(
contact.map(c => shouldRemoveContact(filters, c))
)
// use shouldRemove to check if contact should be removed
// and voila!
return contacts.filter((c, i) => !shouldRemove[i])
}

最新更新