graphhopper/graphhopper-maps

Profile list is empty on iOS

easbar opened this issue · 0 comments

Currently we fetch the /info endpoint with the profiles right at the beginning here:

getApi().infoWithDispatch() // get infos about the api as soon as possible

We already registered the stores at the Dispatcher at this point so this looks ok.

However, we do not subscribe to the ApiInfoStore until the render function in App.tsx is called for the first time here:

getApiInfoStore().register(onInfoChanged)

So apparently it can happen that the /info request returns and the profiles are updated in ApiInfoStore before we registered the setInfo state update function at the ApiInfoStore. In this case the profiles are set correctly in ApiInfoStore, but they never become visible, because we do not call setInfo anymore. For some reason this (only?) happens on iOS mobile devices.

There seem to be two ways two fix this: Either postpone the /info call until the state update callbacks are registered at the stores, i.e. until after this code:

useEffect(() => {
const onQueryChanged = () => setQuery(getQueryStore().state)
const onInfoChanged = () => setInfo(getApiInfoStore().state)
const onRouteChanged = () => setRoute(getRouteStore().state)
const onErrorChanged = () => setError(getErrorStore().state)
const onMapOptionsChanged = () => setMapOptions(getMapOptionsStore().state)
const onPathDetailsChanged = () => setPathDetails(getPathDetailsStore().state)
const onMapFeaturesChanged = () => setMapFeatures(getMapFeatureStore().state)
getQueryStore().register(onQueryChanged)
getApiInfoStore().register(onInfoChanged)
getRouteStore().register(onRouteChanged)
getErrorStore().register(onErrorChanged)
getMapOptionsStore().register(onMapOptionsChanged)
getPathDetailsStore().register(onPathDetailsChanged)
getMapFeatureStore().register(onMapFeaturesChanged)
return () => {
getQueryStore().deregister(onQueryChanged)
getApiInfoStore().deregister(onInfoChanged)
getRouteStore().deregister(onRouteChanged)
getErrorStore().deregister(onErrorChanged)
getMapOptionsStore().deregister(onMapOptionsChanged)
getPathDetailsStore().deregister(onPathDetailsChanged)
getMapFeatureStore().deregister(onMapFeaturesChanged)

or explicitly call all the state update functions (setInfo, setQuery, ...) the first time the render function runs to make sure the stores' state is actually propagated to the app state.

When I tried this I ran into another problem: For some reason the above useEffect in App.tsx has no dependency list, so it runs on every render which resulted in an infinite loop of /info requests when I tried the first approach. I don't know why this is, but I think there should be an empty dependency list to make sure the useEffect runs exactly once.