1

'Once I generate a simple graph in plotly and save it as an html div element, how can I render it in react app? There are questions and solutions with dangerouslySetInnerHTML already here and here. The fact is the react application doens't render it neither throws any error even using library dompurify, so I wonder if I am missing something, or using it is any issue regarding plotly...

Python graph creation with Networkx

import matplotlib.pyplot as plt
import random 
import networkx as nx
import plotly.graph_objects as go
import plotly.io as pio
import plotly

# a simple graph by networkx
G = nx.path_graph(8)

# Graph setup; positions, text, etc
pos = nx.spring_layout(G)
for i in range(len(G.nodes)):
    G.nodes[i]["pos"] = pos[i]

edges = ['E_' + str(i) for i in range(len(G.nodes))]

edge_x = []
edge_y = []
ytext=[]
xtext=[]
for edge in G.edges():
    x0, y0 = pos[edge[0]]
    x1, y1 = pos[edge[1]]
    xtext.append((x0+x1)/2)
    ytext.append((y0+y1)/2)
    edge_x.append(x0)
    edge_x.append(x1)
    edge_x.append(None)
    edge_y.append(y0)
    edge_y.append(y1)
    edge_y.append(None)

# build plotly graph object instance ans setups 
edge_trace = go.Scatter(
    x=edge_x, y=edge_y,
    line=dict(width=0.5, color='#888'),
    hoverinfo='none',
    mode='lines')

node_x = []
node_y = []
for node in G.nodes():
    x, y = pos[node]
    node_x.append(x)
    node_y.append(y)




edge_texts=[f'id: {n}' for n in edges]

edge_trace = go.Scatter(
    x=edge_x, y=edge_y,
    hoverinfo='text',
    text= edge_texts, 
    line=dict(width=0.5, color='#888'),
    textposition='middle center',
    textfont=dict(
        size=5
    ),
    mode='lines')

eweights_trace = go.Scatter(x=xtext, y=ytext,
                            mode='text',
                            hovertext=edges,
                            hoverinfo='text',
                            marker_size=0.5,
                            textposition='top center',
                           )


colormap = ["#FF0000" for i in range(len(G.nodes))]
symbols = [1 for i in range(len(G.nodes))]
names = ["N_"+str(i) for i in G.nodes()]
descriptions = ["foo", "bar", "buz", "qux", "quux", "quuz", "foo", "bar", "buz", "qux", "quux", "quuz"]
types = ["corge", "grault", "garply", "waldo", "fred", "plugh", "xyzzy", "thud", "corge", "grault", "garply", "waldo",]
node_texts=[f'id: {i}<br>Description: {j}<br>Type: {k}<br>Name: {l}<br>Coordination: {m}' for
            i, j, k, l, m in zip(G.nodes(), descriptions, types, names, pos.values())]


node_trace = go.Scatter(
    x=node_x, y=node_y,
    mode='markers',
    marker_symbol=symbols,
    hoverinfo='text+x+y',
    text= node_texts, 
    marker=dict(
        showscale=False,
        colorscale='Hot',
        reversescale=True,
        color=[],
        size=15,
        colorbar=dict(
            thickness=0,
            xanchor='left',
            titleside='right'
        ),
        line_width=2))

node_adjacencies = []
node_text = []
for node, adjacencies in enumerate(G.adjacency()):
    node_adjacencies.append(len(adjacencies[1]))
    node_text.append(descriptions[node])

node_trace.marker.color = colormap

layout=go.Layout(autosize=False,
                 width=500,
                 height=500,
                title='<br>The graph of example',
                titlefont_size=24,
                showlegend=False,
                hovermode='closest',
                margin=dict(l=5,
                            r=5,
                            b=10,
                            t=10,
                            pad = 2),
                annotations=[ dict(
                    showarrow=False,
                    xref="paper", yref="paper",
                    x=0.005, y=-0.002 ) ],
                xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
                yaxis=dict(showgrid=False, zeroline=False, showticklabels=False))

fig = go.Figure(data=[edge_trace, node_trace, eweights_trace], layout=layout); 
# if verification needed
fig.show()

# save it as a div
divastext = plotly.offline.plot(fig, include_plotlyjs=True, output_type='div')

file = open("divastext.txt", "w")
file.write(divastext)
file.close()


JavaScript and React part

import './App.css';
import createDOMPurify from 'dompurify'
import { JSDOM } from 'jsdom'

const window = (new JSDOM('')).window
const DOMPurify = createDOMPurify(window)

function App() {

    getGraph();
    async function getGraph() {
        const response = await fetch("divastext.txt");
        const rawHTML = await response.text();
    }

    return (
        <div className="DivAsText">
            { <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(rawHTML) }} /> }
        </div>
    );
}

export default App;

Is there any more clean way to render this plotly generated div in react? Many thanks in advance.

Shivid
  • 1,295
  • 1
  • 22
  • 36
  • try this once .you have used curly braces out of div also its not required – John Lobo Jun 05 '21 at 16:43
  • 1
    Your `getGraph`-function is asynchronous, so the `rawHTML` variable isn't defined when you render the HTML. You should instead store the html in a state variable instead to ensure React will re-render when the data is received. – CerebralFart Jun 07 '21 at 07:44
  • Thank you, I'll try that. Anyways I tries it without async function too. and I see the div is rendered in DOM but still I don't see it. Tha's why I might be badly wrong on a very elementary thing. – Shivid Jun 07 '21 at 07:58

3 Answers3

1

I think it should be

<div dangerouslySetInnerHTML={{ _html: rawHTML}}></div> 

Since you have wrapped your div in curly braces like below

 { <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(rawHTML_) }} /> }
John Lobo
  • 14,355
  • 2
  • 10
  • 20
1

This is a bit confusing, but I'm thinking you can save the divastext with uppercase and render it as you would any other component: <DivAsText />. Can you try that?

j.oli
  • 36
  • 2
  • sure, but you are referring to this: `const rawHTML_ = loaded contents Of divastext`? This is just a raw text, loaded by fetch. I just avoided to put things that may not have much sense in this context. But this is just anyways a text – Shivid Jun 05 '21 at 16:58
  • I thought you were trying to use this instead of that text from what you wrote: `# save it as a div divastext = plotly.offline.plot(fig, include_plotlyjs=True, output_type='div')` If that is a div than maybe you can use it directly somehow, I'm not really sure though because it's python. That element is probably html and not JSX – j.oli Jun 05 '21 at 17:03
  • Oh sorry I see what you mean, the class... I just removed any class and now is the single element in the dom, but still it is not rendered. The div actually is exist in dom, but has no contents. – Shivid Jun 05 '21 at 17:03
  • You are right, maybe I explained a bit confused. That part of code is in python to generate graph. – Shivid Jun 05 '21 at 17:04
  • Sorry, I was having problems writing that comment! Can you show the element on the dom or something? – j.oli Jun 05 '21 at 17:07
  • yes yes, it is on the dom. but hoveing on it, it has no content. – Shivid Jun 05 '21 at 17:23
  • That's weird, what does the python output in the divastext? – j.oli Jun 05 '21 at 17:27
  • it is an ordinary python str. sorry for late reply. – Shivid Jun 05 '21 at 19:42
0

I figured out you can also use an iframe to render your html-plots. You can do it like this in typescript:

//importing a html in typescript requires a "html-loader"
import plot from "./someplot"

   
function getPlotDiv() {
  const formattedHtml = plot.toString();
  return (
    <div>
      <iframe srcdoc={formattedHtml} width="750pt" height="400pt"></iframe>
    </div>
  );
}

The export of the plot in python would than look something like that:

plot.write_html(fig, file='./someplot.html', auto_open=False, full_html=True, include_plotlyjs=True)
didi022
  • 1
  • 1