How to Pass JSON to Custom Component

I've created a custom component in order to display a custom map using the Google Maps API. (I'm using geojson for my location data.) If I hardcode in the data in my component, it works as expected. However, when I try to send that data from my Retool app to the custom component, it doesn't work. I either get a map with no data points or no map with errors.

I've tried to send it as an array, an object, and a string, but nothing seems to work. I've also tried to send it as an object and then convert that object into json within my component, but that doesn't work either.

Does anyone know how to go about doing this? I've read through the custom component documentation several times, but there's nothing specific enough to help me with this. I've also read through as many similar topics here in the forum as I can find, but they either are for the legacy custom component or don't address my question.

Here's a screenshot of my custom component in my app with sample data I've tried to push through. (This is what it looks like when I hardcode the data into the component.)

And here's the index.tsx file for my custom component, with my latest attempt to JSON.stringify() the object received from my app:

import React, {Ref, useCallback, useEffect, useState} from 'react'
import { type FC } from 'react'
import { Retool } from '@tryretool/custom-component-support'
import {createRoot} from 'react-dom/client'
import {APIProvider, InfoWindow, Map} from '@vis.gl/react-google-maps'
import {ClusteredMarkers} from './components/clustered-markers'
import ControlPanel from './control-panel'
import './style.css'
import {Feature, Point} from 'geojson'
import {InfoWindowContent} from './components/info-window-content'

export const StudentMap: FC = () => {
  const [points, _setPoints] = Retool.useStateObject({
    name: 'Points'
  })
  
  //const data = JSON.stringify({points})
  const data = {points}

  const [geojson, setGeojson] = useState<StudentsGeojson | null>(null)
  const [numClusters, setNumClusters] = useState(0)

  useEffect(() => {
    setGeojson(data);
  }, []);

  const [infowindowData, setInfowindowData] = useState<{
    anchor: google.maps.marker.AdvancedMarkerElement
    features: Feature<Point>[]
  } | null>(null);

  const handleInfoWindowClose = useCallback(
    () => setInfowindowData(null),
    [setInfowindowData]
  );

  return (
    <APIProvider apiKey={'AIzaSyAG3yXfRgPoVvteWl08sPUIQK0F4fDpDu0'} version={'beta'}>
      <Map
        mapId={'b5387d230c6cf22f'}
        defaultCenter={{lat: 34.6051422, lng: -112.3313968}}
        defaultZoom={4}
        gestureHandling={'greedy'}
        disableDefaultUI
        onClick={() => setInfowindowData(null)}
        className={'custom-marker-clustering-map'}>
        {geojson && (
          <ClusteredMarkers
            geojson={geojson}
            setNumClusters={setNumClusters}
            setInfowindowData={setInfowindowData}
          />
        )}

        {infowindowData && (
          <InfoWindow
            onCloseClick={handleInfoWindowClose}
            anchor={infowindowData.anchor}>
            <InfoWindowContent features={infowindowData.features} />
          </InfoWindow>
        )}
      </Map>
      
      <ControlPanel
        numClusters={numClusters}
        numFeatures={geojson?.features.length || 0}
      />
    </APIProvider>
  )
}

export function renderToDom(container: HTMLElement) {
  const root = createRoot(container)

  root.render(
    <React.StrictMode>
      <StudentMap />
    </React.StrictMode>
  );
}

Hey @Jennifer_FreedomPrep! Thanks for reaching out - I'm always excited to help people out with custom components. :slightly_smiling_face:

When you successfully hard code the geojson variable, are you passing it into ClusteredMarkers as an object or as a string? I'm guessing the former, but wanted to double check.

My initial recommendation is to ensure that geojson is being properly set whenever data is sent down into the component, by modifying your useEffect statement to something like the following.

  useEffect(() => {
    setGeojson(points);
  }, [points]);

Let me know if that makes a difference! I'm happy to take a deeper dive, if needed.