根据 Google 地图 API 结果更改 table 数据集
Change table data set based on Google Maps API results
我正在尝试实现一个过滤器,它允许我根据位置与其地址之间的距离来过滤用户。使用useMemo向table提供数据,基本上是这样的:
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]
);
这工作正常,在控制台中结果 return 正如我所期望的那样。但是,我的 table 没有更新。我怀疑这是因为 API 调用的结果是异步的,因此 table 在结果进入之前重新呈现。
我曾尝试使用 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
的 return 完成后运行。所以我的日志看起来像这样(我目前有 16 contacts/users):
- 日志:在useEffect()中
- 日志:状态已更改,过滤将开始
- (16)日志:即将return返回值
- 日志:计算距离:1324
- 日志:计算距离:4326
- ...
所以基本上,它仍然忽略了我函数的异步状态 calculateDistance
。有什么想法吗?
编辑 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
将始终 return 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])
}
我正在尝试实现一个过滤器,它允许我根据位置与其地址之间的距离来过滤用户。使用useMemo向table提供数据,基本上是这样的:
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]
);
这工作正常,在控制台中结果 return 正如我所期望的那样。但是,我的 table 没有更新。我怀疑这是因为 API 调用的结果是异步的,因此 table 在结果进入之前重新呈现。
我曾尝试使用 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
的 return 完成后运行。所以我的日志看起来像这样(我目前有 16 contacts/users):
- 日志:在useEffect()中
- 日志:状态已更改,过滤将开始
- (16)日志:即将return返回值
- 日志:计算距离:1324
- 日志:计算距离:4326
- ...
所以基本上,它仍然忽略了我函数的异步状态 calculateDistance
。有什么想法吗?
编辑 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
将始终 return 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])
}