【发布时间】:2021-12-25 18:09:22
【问题描述】:
我正在尝试实现一个过滤器,它允许我根据位置与其地址之间的距离过滤用户。使用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 个联系人/用户):
- 日志:在 useEffect() 中
- 日志:状态已更改,将开始过滤
- (16) 日志:即将返回shouldReturn值
- 日志:计算距离: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) = () => {
【问题讨论】:
标签: reactjs google-maps-api-3 react-hooks