Skip to content

Commit

Permalink
Use dangerouslySetInnerHTML instead of setting innerHTML, this fixes …
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin committed Apr 10, 2023
1 parent 4c6a784 commit a3b06df
Show file tree
Hide file tree
Showing 15 changed files with 1,462 additions and 1,477 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useRef } from 'react'
import { observer } from 'mobx-react'
import { hydrate, unmountComponentAtNode } from 'react-dom'
import { AnyReactComponentType, Feature, rIC } from '../../util'
import { AnyReactComponentType, Feature } from '../../util'

export default observer(function (props: {
html: string
Expand All @@ -12,31 +12,34 @@ export default observer(function (props: {
const ref = useRef<SVGGElement>(null)
useEffect(() => {
const domNode = ref.current
function doHydrate() {
if (domNode && html) {
if (domNode.innerHTML) {
unmountComponentAtNode(domNode)
}

// setting outline:none fixes react "focusable" element issue. see
// https://github.com/GMOD/jbrowse-components/issues/2160
domNode.style.outline = 'none'
domNode.innerHTML = html
// use requestIdleCallback to defer main-thread rendering
// and hydration for when we have some free time. helps
// keep the framerate up.
rIC(() => {
hydrate(<RenderingComponent {...props} />, domNode)
})
}
if (!domNode) {
return
}
if (domNode.innerHTML) {
unmountComponentAtNode(domNode)
}
doHydrate()
// setting outline:none fixes react "focusable" element issue. see
// https://github.com/GMOD/jbrowse-components/issues/2160
domNode.style.outline = 'none'
// use requestIdleCallback to defer main-thread rendering
// and hydration for when we have some free time. helps
// keep the framerate up.
hydrate(<RenderingComponent {...props} />, domNode)

return () => {
if (domNode) {
unmountComponentAtNode(domNode)
// use setTimeout to try to avoid error :unmounted component rendered
// by another copy of react error, even when that is not the case. See
// https://github.com/facebook/react/issues/22343#issuecomment-924098716
// and specifically that comment

setTimeout(() => {
unmountComponentAtNode(domNode)
}, 0)
}
}
}, [html, RenderingComponent, props])

return <g ref={ref} />
// eslint-disable-next-line react/no-danger
return <g ref={ref} dangerouslySetInnerHTML={{ __html: html }} />
})
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { hydrate, unmountComponentAtNode } from 'react-dom'

// locals
import { createJBrowseTheme } from '../../ui'
import { rIC } from '../../util'
import { ResultsSerialized, RenderArgs } from './ServerSideRendererType'

interface Props extends ResultsSerialized, RenderArgs {
Expand All @@ -14,46 +13,38 @@ interface Props extends ResultsSerialized, RenderArgs {

export default function ({ theme, html, RenderingComponent, ...rest }: Props) {
const ref = useRef<HTMLDivElement>(null)
const jbrowseTheme = createJBrowseTheme(theme)

useEffect(() => {
const domNode = ref.current
function doHydrate() {
if (domNode) {
if (domNode) {
unmountComponentAtNode(domNode)
}
domNode.innerHTML = html

// defer main-thread rendering and hydration for when
// we have some free time. helps keep the framerate up.
//
// note: the timeout param to rIC below helps when you are doing
// a long continuous scroll, it forces it to evaluate because
// otherwise the continuous scroll would never give it time to do
// so
rIC(
() => {
hydrate(
<ThemeProvider theme={jbrowseTheme}>
<RenderingComponent {...rest} />
</ThemeProvider>,
domNode,
)
},
{ timeout: 300 },
)
}
}

doHydrate()
if (!domNode) {
return
}
if (domNode.innerHTML) {
unmountComponentAtNode(domNode)
}

const jbrowseTheme = createJBrowseTheme(theme)
hydrate(
<ThemeProvider theme={jbrowseTheme}>
<RenderingComponent {...rest} />
</ThemeProvider>,
domNode,
)
return () => {
if (domNode) {
unmountComponentAtNode(domNode)
// use setTimeout to try to avoid error :unmounted component rendered
// by another copy of react error, even when that is not the case. See
// https://github.com/facebook/react/issues/22343#issuecomment-924098716
// and specifically that comment

setTimeout(() => {
unmountComponentAtNode(domNode)
}, 0)
}
}
}, [html, jbrowseTheme, rest, RenderingComponent])
}, [theme, rest, RenderingComponent])

return <div ref={ref} />
// eslint-disable-next-line react/no-danger
return <div ref={ref} dangerouslySetInnerHTML={{ __html: html }} />
}
10 changes: 3 additions & 7 deletions packages/core/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
IAnyStateTreeNode,
IStateTreeNode,
} from 'mobx-state-tree'
import { reaction, IReactionPublic, IReactionOptions } from 'mobx'
import { reaction, IReactionOptions } from 'mobx'
import { Feature } from './simpleFeature'
import {
isSessionModel,
Expand Down Expand Up @@ -645,7 +645,6 @@ export function makeAbortableReaction<T, U, V>(
arg: U | undefined,
signal: AbortSignal,
model: T,
handle: IReactionPublic,
) => Promise<V>,
// @ts-expect-error
reactionOptions: IReactionOptions,
Expand All @@ -657,10 +656,9 @@ export function makeAbortableReaction<T, U, V>(

function handleError(error: unknown) {
if (!isAbortException(error)) {
console.error(error)
if (isAlive(self)) {
errorFunction(error)
} else {
console.error(error)
}
}
}
Expand All @@ -676,7 +674,7 @@ export function makeAbortableReaction<T, U, V>(
return undefined
}
},
async (data, mobxReactionHandle) => {
async data => {
if (inProgress && !inProgress.signal.aborted) {
inProgress.abort()
}
Expand All @@ -693,8 +691,6 @@ export function makeAbortableReaction<T, U, V>(
data,
thisInProgress.signal,
self,
// @ts-expect-error
mobxReactionHandle,
)
checkAbortSignal(thisInProgress.signal)
if (isAlive(self)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1262,7 +1262,6 @@ exports[`ConfigurationEditor widget renders with defaults of the PileupTrack sch
</div>
<input
aria-hidden="true"
aria-invalid="false"
class="MuiSelect-nativeInput css-yf8vq0-MuiSelect-nativeInput"
tabindex="-1"
value="PileupRenderer"
Expand Down Expand Up @@ -1428,7 +1427,6 @@ exports[`ConfigurationEditor widget renders with defaults of the PileupTrack sch
</div>
<input
aria-hidden="true"
aria-invalid="false"
class="MuiSelect-nativeInput css-yf8vq0-MuiSelect-nativeInput"
tabindex="-1"
value="fr"
Expand Down Expand Up @@ -1505,7 +1503,6 @@ exports[`ConfigurationEditor widget renders with defaults of the PileupTrack sch
</div>
<input
aria-hidden="true"
aria-invalid="false"
class="MuiSelect-nativeInput css-yf8vq0-MuiSelect-nativeInput"
tabindex="-1"
value="normal"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ exports[`<AddConnectionWidget /> renders 1`] = `
</div>
<input
aria-hidden="true"
aria-invalid="false"
class="MuiSelect-nativeInput css-yf8vq0-MuiSelect-nativeInput"
tabindex="-1"
value="UCSCTrackHubConnection"
Expand Down
Loading

0 comments on commit a3b06df

Please sign in to comment.