Skip to content

Commit

Permalink
Use react event delegation for UJS
Browse files Browse the repository at this point in the history
This fixes issues with react portals that are rendered
outside the mounted component. By using an onclick on a div
that houses the component, we can capture child events even
if they are rendered outside.
  • Loading branch information
jho406 committed Dec 12, 2024
1 parent b33b7f6 commit 89640aa
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 39 deletions.
61 changes: 28 additions & 33 deletions superglue/lib/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useMemo } from 'react'
import React, { useRef, useMemo } from 'react'
import parse from 'url-parse'
import { config } from './config'
import { urlToPageKey, ujsHandlers, argsForHistory } from './utils'
Expand Down Expand Up @@ -100,58 +100,53 @@ function Application({
buildVisitAndRemote,
history,
mapping,
appEl,
...rest
}: ApplicationProps) {
const navigatorRef = useRef<{ navigateTo: NavigateTo }>(null)

const { visit, remote, nextHistory, connectedMapping, initialPageKey } =
const { visit, remote, nextHistory, connectedMapping, initialPageKey, ujs } =
useMemo(() => {
config.baseUrl = baseUrl

const {visit, remote} = buildVisitAndRemote(navigatorRef, store)

const initialPageKey = urlToPageKey(parse(path).href)
const nextHistory = history || createHistory()
nextHistory.replace(...argsForHistory(path))
prepareStore(store, initialPage, path)

const handlers = ujsHandlers({
visit,
remote,
ujsAttributePrefix: 'data-sg',
store,
})

return {
...buildVisitAndRemote(navigatorRef, store),
visit,
remote,
connectedMapping: connectMapping(mapping),
nextHistory,
initialPageKey,
ujs: handlers
}
}, [])

useEffect(() => {
const handlers = ujsHandlers({
visit,
remote,
ujsAttributePrefix: 'data-sg',
store,
})

const { onClick, onSubmit } = handlers
appEl.addEventListener('click', onClick)
appEl.addEventListener('submit', onSubmit)

return () => {
appEl.removeEventListener('click', onClick)
appEl.removeEventListener('submit', onSubmit)
}
}, [])


// The Nav component is pretty bare and can be inherited from for custom
// behavior or replaced with your own.
return (
<Provider store={store}>
<NavigationProvider
ref={navigatorRef}
visit={visit}
remote={remote}
mapping={connectedMapping}
history={nextHistory}
initialPageKey={initialPageKey}
/>
</Provider>
<div onClick={ujs.onClick} onSubmit={ujs.onSubmit} {...rest}>
<Provider store={store}>
<NavigationProvider
ref={navigatorRef}
visit={visit}
remote={remote}
mapping={connectedMapping}
history={nextHistory}
initialPageKey={initialPageKey}
/>
</Provider>
</div>
)
}

Expand Down
4 changes: 2 additions & 2 deletions superglue/lib/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,8 @@ export type SuperglueStore = EnhancedStore<
>

export interface Handlers {
onClick: (event: MouseEvent) => void
onSubmit: (event: Event) => void
onClick: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
onSubmit: (event: React.FormEvent<HTMLDivElement>) => void
}

export type UJSHandlers = ({
Expand Down
10 changes: 6 additions & 4 deletions superglue/lib/utils/ujs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ export class HandlerBuilder {
}
}

isNonStandardClick(event: MouseEvent): boolean {
isNonStandardClick(
event: React.MouseEvent<HTMLDivElement, MouseEvent>
): boolean {
return (
event.which > 1 ||
event.button > 0 ||
event.metaKey ||
event.ctrlKey ||
event.shiftKey ||
Expand All @@ -62,7 +64,7 @@ export class HandlerBuilder {
return hasVisit || hasRemote
}

handleSubmit(event: Event): void {
handleSubmit(event: React.FormEvent<HTMLDivElement>): void {
const form = event.target

if (!(form instanceof HTMLFormElement)) {
Expand All @@ -89,7 +91,7 @@ export class HandlerBuilder {
})
}

handleClick(event: MouseEvent): void {
handleClick(event: React.MouseEvent<HTMLDivElement, MouseEvent>): void {
if (!(event.target instanceof Element)) {
return
}
Expand Down

0 comments on commit 89640aa

Please sign in to comment.