rnmapbox / rnmapbox/maps

[Bug]: multiple async URI in <images> do not render on android

Open
#4,062 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug 🪲
Dominant language
Kotlin
Stars
2.9k
Forks
947
Avg merge
6d 37m
Merged PRs (30d)
1

Description

Mapbox Implementation

Mapbox

Mapbox Version

11.15.3

React Native Version

0.81.4

React Native Architecture

New Architecture (Fabric/TurboModules)

Platform

Android

@rnmapbox/maps version

10.2.5

Standalone component to reproduce
// BugReportExampleSymbols.js (pure JS)
import React, {useCallback, useMemo, useState} from 'react';
import {
  MapView,
  ShapeSource,
  SymbolLayer,
  Camera,
  Images,
} from '@rnmapbox/maps';
import {View, Button, StyleSheet} from 'react-native';

// If needed in your app entry, remember to set your token:
// import Mapbox from '@rnmapbox/maps';
// Mapbox.setAccessToken('YOUR_TOKEN');

const CENTER = [-74.00597, 40.71427];
const PIN_URL =
  'https://raw.githubusercontent.com/rnmapbox/maps/refs/heads/main/example/src/assets/pin.png';

const EMPTY_FC = {
  type: 'FeatureCollection',
  features: [],
};

const layerStyle = {
  iconImage: ['get', 'icon'], // read image id from feature properties.icon
  iconAllowOverlap: true,
  iconIgnorePlacement: true,
  iconSize: 0.7,
};

function makeRandomPoints(count, around) {
  const [lng, lat] = around;
  const features = [];

  for (let i = 0; i < count; i++) {
    // small jitter around center
    const dx = (Math.random() - 0.5) * 0.01;
    const dy = (Math.random() - 0.5) * 0.01;
    features.push({
      type: 'Feature',
      id: `pt-${i}`,
      properties: {
        icon: `pin-${i}`, // unique image id per point
      },
      geometry: {
        type: 'Point',
        coordinates: [lng + dx, lat + dy],
      },
    });
  }
  return {type: 'FeatureCollection', features};
}

export default function BugReportExampleSymbols() {
  const [fc, setFc] = useState(EMPTY_FC);
  const [imgs, setImgs] = useState({});

  const loadPointsAndImages = useCallback(() => {
    // 1) Prepare 10 unique image IDs (all backed by the SAME URL)
    const nextImgs = {};
    for (let i = 0; i < 10; i++) {
      // cache-bust query so RN treats them as distinct if needed
      nextImgs[`pin-${i}`] = {uri: `${PIN_URL}?v=${i}`};
    }
    setImgs(nextImgs);

    // 2) Create 10 point features that reference the above image ids
    setFc(makeRandomPoints(10, CENTER));
  }, []);

  const clearAll = useCallback(() => {
    setImgs({});
    setFc(EMPTY_FC);
  }, []);

  const camera = useMemo(
    () => <Camera centerCoordinate={CENTER} zoomLevel={14} />,
    [],
  );

  return (
    <View style={styles.container}>
      <View style={styles.row}>
        <Button title="Load 10 points + images" onPress={loadPointsAndImages} />
        <View style={styles.spacer} />
        <Button title="Clear" onPress={clearAll} />
      </View>

      <MapView style={styles.map}>
        {camera}

        {/* Provide images so SymbolLayer can resolve icon ids */}
        <Images images={imgs} />

        <ShapeSource id="bug-symbol-source" shape={fc}>
          <SymbolLayer id="bug-symbol-layer" style={layerStyle} />
        </ShapeSource>
      </MapView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {flex: 1},
  row: {
    flexDirection: 'row',
    padding: 8,
    gap: 8,
    backgroundColor: '#ffffff',
    zIndex: 1,
  },
  spacer: {width: 8},
  map: {flex: 1},
});


Observed behavior and steps to reproduce

The images are resolved to a URI and set on the <images> component. But the images are never rendered to the screen or if they are only 1 or two is.

To reproduce, just run the code and then click the button to load the pins.

Expected behavior

It should render the pins on the map.

Notes / preliminary analysis

It appears to be a race condition of some sort because if i pause the code after react starts processing the set images it will render them all.

Additional links and references

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the reproduction in BugReportExampleSymbols.js on Android and pressing “Load 10 points + images.” Read the @rnmapbox/maps Images component and the SymbolLayer usage shown in the example. Done means all 10 pins render reliably rather than only one or two.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, javascript, react-native
Domain
mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.