Accessing state of custom component

I have been bashing my head against a brick wall all day trying to figure this out. I have created a custom component called SortablePhotos, and I want to expose a variable when the user clicks on a particular image, namely the ID of the image and the URL — so that I can pop up a large version of the image in a model, and be able to use the ID to delete the image from a GraphQL API.

When I hover over the expression I want to use, e.g. {{ listingPhotos.selectedItemUrl }} the value resolves just fine:

But when I try to actually use it on another component the variable is empty. I tried then adding a textbox with a value of {{ listingPhotos.selectedItemUrl }}, and the textbox turns red and gives me the error below

Retool.useEventCallback doesn't provide the option to return data with an event either, so I feel like I am out of options.

Is there any fix/way around this?

const SortablePhotos = () => {
  const onSortEnd = Retool.useEventCallback({ name: "dragEnd" })
  const onClick = Retool.useEventCallback({ name: "click" })

  const [data, setData] = Retool.useStateArray({
    name: "data",
    initialValue: [],
  })

  const [, setSelectedItemId] = Retool.useStateString({
    name: "selectedItemId",
    initialValue: "",
    inspector: "hidden",
  })

  const [, setSelectedItemUrl] = Retool.useStateString({
    name: "selectedItemUrl",
    initialValue: "",
    inspector: "hidden",
  })

  const handleSortEnd = useCallback(
    (oldIndex: number, newIndex: number) => {
      const sortedData = arrayMoveImmutable(data, oldIndex, newIndex)
      setData(sortedData)

      // We need to use setTimeout to make sure the dragEnd event is called after the state is updated
      setTimeout(() => onSortEnd(), 100)
    },
    [setData, data]
  )

  const handleClick = (photo: Photo) => () => {
    setSelectedItemId(photo.id)
    setSelectedItemUrl(photo.cloudinaryURL)
    setTimeout(() => onClick(), 100)
  }

  return (
    <SortableList
      onSortEnd={handleSortEnd}
      className={styles["authentified-sortable-photo-list"]}
      draggedItemClassName="authentified-sortable-photo-list-item--dragged"
    >
      <style>
        {`
        .authentified-sortable-photo-list-item--dragged {
          cursor: grabbing;
          opacity: 0.5;
        }`}
      </style>
      {data.map((photo) => (
        <SortableItem key={photo.cloudinaryURL}>
          <div className={styles["authentified-sortable-photo-list-item"]} onClick={handleClick(photo)}>
            <img src={photo.cloudinaryURL} className={styles["authentified-sortable-photo-list-item-image"]} />
          </div>
        </SortableItem>
      ))}
    </SortableList>
  )
}

export { SortablePhotos }

Of course within 20 minutes of writing this I solved it.

Adding useCallback to the event and passing the id/url and setters to the dependency list fixed it.

Before:

  const [, setSelectedItemId] = Retool.useStateString({
    name: "selectedItemId",
    initialValue: "",
    inspector: "hidden",
  })

  const [, setSelectedItemUrl] = Retool.useStateString({
    name: "selectedItemUrl",
    initialValue: "",
    inspector: "hidden",
  })

  const handleClick = (photo: Photo) => () => {
    setSelectedItemId(photo.id)
    setSelectedItemUrl(photo.cloudinaryURL)
    setTimeout(() => onClick(), 100)
  }

After:

  const [selectedItemId, setSelectedItemId] = Retool.useStateString({
    name: "selectedItemId",
    initialValue: "",
    inspector: "hidden",
  })

  const [selectedItemUrl, setSelectedItemUrl] = Retool.useStateString({
    name: "selectedItemUrl",
    initialValue: "",
    inspector: "hidden",
  })

  const handleClick = useCallback(
    (photo: Photo) => () => {
      setSelectedItemId(photo.id)
      setSelectedItemUrl(photo.cloudinaryURL)
      setTimeout(() => onClick(), 100)
    },
    [selectedItemId, selectedItemUrl, setSelectedItemId, setSelectedItemUrl]
  )
3 Likes

Thanks for sharing an update, @authentified-dave! Can't tell you the number of times I've answered my own question or debugged faulty code just by vocalizing it or walking somebody else through it.