【问题标题】:Change table data set based on Google Maps API results根据 Google Maps API 结果更改表数据集
【发布时间】: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


    【解决方案1】:

    您的错误是正确的 - 您的 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])
       }
    

    【讨论】:

    • 嗨@Secret,谢谢!我明白了这一点,所以它对我有很大帮助。但是,我似乎有几个语法错误。第一个出现在const shouldRemoveContact = await (filters, contact) => { 中的=>,我对异步函数了解不多,无法修复它。认为应该是const shouldRemoveContact = await (filters, contact) = () => { 但这给我带来了其他错误,所以我不确定如何继续。我已经用我当前的代码更新了原始问题,它没有给我语法错误,但是如下:Syntax error: Unexpected reserved word 'await'. 在第一行。
    • 嗯,我已经多次阅读您的评论,我将不得不给它一些适当的想法,而不是试图快速修复它。这个周末我会潜水。
    • @PennyWise 绝对尝试花一些时间来理解它 - 修复有点“棘手”,但一旦你理解它就会很有意义!顺便说一句,我犯了一个错误!应该是async () => {}(我更新了我的答案)
    • 我还写了伪代码说明解决方案背后的想法是什么
    • 我已接受您的回答。然而,看起来实际结果(距离,来自calculateDistance 函数),在所有promise 都被解决之后仍然是结果。但我觉得你给了我很多信息,所以我会在接下来的几天里尝试找到解决方案。非常感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 2014-02-18
    • 1970-01-01
    • 1970-01-01
    • 2020-09-26
    • 1970-01-01
    • 1970-01-01
    • 2015-06-21
    • 1970-01-01
    相关资源
    最近更新 更多