Custom component: Passing Dash components as html?

I am trying to implement a Dash version of the DivIcon component. One of its arguments is of the type String|HTMLElement. Hence, if i pass it an html string, the html will be rendered as intended. However, i would rather pass it Dash components (this would be more user friendly).

As a starting point, i tried passing the components via the children property of the Dash component, and then reassigning it in the JavaScript layer, i.e. something like

render() {
    const nProps = Object.assign({}, this.props, {"children": null});
    nProps.icon = new L.DivIcon({options:{html:this.props.children}});
    ...
    return <LeafletMarker {...nProps}/>
}

But (unsurprisingly) that does not work. Is there any way to achieve this behavior in Dash?

Do you, @chriddyp, know if this kind of use case is possible with the current version of Dash?

Unfortunately it’s not available by default.

However, components get serialized as json and so in theory you could transform that json into a react component call. In other words, think about children as if it is just some nested object / dictionary property. It has keys “namespace”, “type”, and “props”
There are a few limitations to this:

  • You won’t be able to update or observe the rendered components via callbacks. Dash introspects/crawls children to do this, it won’t crawl through any other component hierarchy
  • To handle nested components, you’ll need to crawl / recurse through the objects yourself and render them
  • Children may come to you, the component, as an already-rendered React element instead of its JSON representation. Im not sure here, this has changed a couple of times over the years. If it’s a rendered component, that’ll be harder to extract the properties from but you may be able to just render it directly. However, there is also this prop called something like _private_dash_layout (i’m on my phone and so i don’t remember exactly) that might contain the component in json form. Check out the source of the Tabs component, I believe that uses this property. Beware, this is a private property and so we may change it at any time without advance notice.
1 Like

Thanks for the elaborate answer! I ended up with the following approach,

  • Pass the objects from Dash via the children property to the JS layer
  • Create a new div element in JS, and assign the objects as children of this div with the rendering carried out via ReactDOM.createPortal

It feels a bit hacky, but at least it works :slight_smile:

1 Like

Just to clarify, even if i manage to convert my dash to ReactElement inside my custom component, being sent via a callback to a custom property, even if this gets rendered correctly, all the callbacks wont work here?

I was trying a different solution by rending my dash to a div which is hidden, and then triggering my custom component, trying to grab the html and “inject” it into the space i need, problem is i need it as a ReactElement if i want the custom component to be able to persist this new element and “know” of it. otherwise it reverts to a state before my hack

Yes, that is also my understanding (that callbacks won’t work)

1 Like

What if you could use pattern-matching somehow for the callbacks?

I guess it really depends upon what the end goal of the callback is.

I have a custom react component, and I would like to be able to pass the dash html down into this component, and still have it render correctly with dash and respond to callbacks

Dash’s callbacks, as far as I understand it, are just post requests to specific endpoints with payloads.

How are moving it over into the other component?

I want to use the rc-dock library. I am wrapping it myself and have that part working. What I cannot get working is being able to pass it the html part that i want inside of each “tab”. It requires me to pass a type of ReactElement, which I thought I could somehow pass it from my dash app since it is in react, but I have not managed that part

1 Like

How are you trying to pass the elements over to it?

Is it possible for you to alter the flask template for the react components to take advantage of both? Or you need the rc-dock to be driving what tabs the elements are in?

rc-dock needs to have the “content” set internally when I add a new tab. My UI is dynamic and users can add/remove dashboard panels. the panel needs to be “passed” to rc-dock in order to render it inside of the panel that it generates when the user clicks the add button

Hmm, I was thinking it would only have to render the tab and you could migrate the divs over via JS to their target tabs. Have you tried this?

I have and that worked…until i start interact with the dock and do resizing. it re-renders what it has in its state (which is not the code I moved there)

Ok, cool.

By not resizing, have you passed style arguments to the items being rendered in dash?

its not just about resizing. it also has tabs end a lot of other features. i am trying to find a solution not a workaround :grinning:

What about the callbacks, did they trigger when you tried that? If they triggered, were they able to update the elements as desired as well?

By triggered, I mean, was there a post request sent to the server.

I dont recall, I can check soon just on a deadline on another branch. but how will that help?

I am not sure if it is possible to wrap the rc-dock based on how it was designed and how i need to pass the content to a new panel when it gets created. this content is meant to be the children component of the panel but i dont think i can pass it\

defaultLayout = {
  dockbox: {
    mode: 'horizontal',
    children: [
      {
        tabs: [
          {id: 'tab1', title: 'tab1', content: <div>Hello World</div>}
        ]
      }
    ]
  }
};

render() {
  return (
    <DockLayout
      defaultLayout={defaultLayout}
      style={{
        position: "absolute",
        left: 10,
        top: 10,
        right: 10,
        bottom: 10,
      }}
    />
  )
}

1 - I am curious. I know that if I alter don elements on the JS side, callbacks are triggered but the updates don’t always translate.

2 - if they do trigger but don’t update, there can be a way to perform the desired updates, either by going through the response and updating the props from there.

Yeah, I saw your other post about using draggable instead.

even if number 1 works, the problem is that the component re-renders the content based on the tabs being displayed and other manipulations in the rc-dock, so it might work for a while, but after a resize, or re-render of the rc-dock, my changes get lost and the default content gets displayed. I need a way to persist this change into the rc-dock