Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-panels-stay-independent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/query-devtools': patch
---

Isolate the signals and subscriptions used across the DevTools context
144 changes: 62 additions & 82 deletions packages/query-devtools/src/Devtools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import { Key } from '@solid-primitives/keyed'
import { createResizeObserver } from '@solid-primitives/resize-observer'
import { DropdownMenu, RadioGroup } from '@kobalte/core'
import { Portal } from 'solid-js/web'
import { useDevtoolsOffline } from './contexts/DevtoolsOfflineContext'
import { useDevtoolsSubscriptions } from './contexts/DevtoolsSubscriptionsContext'
import { useDevtoolsState } from './contexts/DevtoolsStateContext'
import { tokens } from './theme'
import {
convertRemToPixels,
Expand Down Expand Up @@ -78,7 +81,7 @@ import type {
QueryCacheNotifyEvent,
} from '@tanstack/query-core'
import type { StorageObject, StorageSetter } from '@solid-primitives/storage'
import type { Accessor, Component, JSX, Setter } from 'solid-js'
import type { Accessor, Component, JSX } from 'solid-js'

interface DevtoolsPanelProps {
localStore: StorageObject<string>
Expand All @@ -98,15 +101,6 @@ interface QueryStatusProps {
count: number
}

const [selectedQueryHash, setSelectedQueryHash] = createSignal<string | null>(
null,
)
const [selectedMutationId, setSelectedMutationId] = createSignal<number | null>(
null,
)
const [panelWidth, setPanelWidth] = createSignal(0)
const [offline, setOffline] = createSignal(false)

export type DevtoolsComponentType = Component<QueryDevtoolsProps> & {
shadowDOMTarget?: ShadowRoot
}
Expand All @@ -119,19 +113,6 @@ export const Devtools: Component<DevtoolsPanelProps> = (props) => {
const styles = createMemo(() => {
return theme() === 'dark' ? darkStyles(css) : lightStyles(css)
})
const onlineManager = createMemo(
() => useQueryDevtoolsContext().onlineManager,
)
onMount(() => {
const unsubscribe = onlineManager().subscribe((online) => {
setOffline(!online)
})

onCleanup(() => {
unsubscribe()
})
})

const pip = usePiPWindow()

const buttonPosition = createMemo(() => {
Expand Down Expand Up @@ -285,6 +266,8 @@ export const Devtools: Component<DevtoolsPanelProps> = (props) => {
const PiPPanel: Component<{
children: JSX.Element
}> = (props) => {
const { panelWidth, setPanelWidth } = useDevtoolsState()

const pip = usePiPWindow()
const theme = useTheme()
const css = useQueryDevtoolsContext().shadowDOMTarget
Expand Down Expand Up @@ -355,6 +338,8 @@ const PiPPanel: Component<{
export const ParentPanel: Component<{
children: JSX.Element
}> = (props) => {
const { panelWidth, setPanelWidth } = useDevtoolsState()

const theme = useTheme()
const css = useQueryDevtoolsContext().shadowDOMTarget
? goober.css.bind({ target: useQueryDevtoolsContext().shadowDOMTarget })
Expand Down Expand Up @@ -412,6 +397,8 @@ export const ParentPanel: Component<{
}

const DraggablePanel: Component<DevtoolsPanelProps> = (props) => {
const { setSelectedQueryHash, panelWidth, setPanelWidth } = useDevtoolsState()

const theme = useTheme()
const css = useQueryDevtoolsContext().shadowDOMTarget
? goober.css.bind({ target: useQueryDevtoolsContext().shadowDOMTarget })
Expand Down Expand Up @@ -676,6 +663,16 @@ const DraggablePanel: Component<DevtoolsPanelProps> = (props) => {
}

export const ContentView: Component<ContentViewProps> = (props) => {
const {
selectedQueryHash,
setSelectedQueryHash,
selectedMutationId,
setSelectedMutationId,
panelWidth,
} = useDevtoolsState()

const { offline, toggleOffline } = useDevtoolsOffline()

setupQueryCacheSubscription()
setupMutationCacheSubscription()
let containerRef!: HTMLDivElement
Expand Down Expand Up @@ -710,10 +707,6 @@ export const ContentView: Component<ContentViewProps> = (props) => {
() => mutationSortFns[mutationSort() as string],
)

const onlineManager = createMemo(
() => useQueryDevtoolsContext().onlineManager,
)

const query_cache = createMemo(() => {
return useQueryDevtoolsContext().client.getQueryCache()
})
Expand Down Expand Up @@ -1035,9 +1028,7 @@ export const ContentView: Component<ContentViewProps> = (props) => {
<Trash />
</button>
<button
onClick={() => {
onlineManager().setOnline(!onlineManager().isOnline())
}}
onClick={toggleOffline}
class={cx(
styles().actionsBtn,
offline() && styles().actionsBtnOffline,
Expand Down Expand Up @@ -1376,6 +1367,8 @@ export const ContentView: Component<ContentViewProps> = (props) => {
}

const QueryRow: Component<{ query: Query }> = (props) => {
const { selectedQueryHash, setSelectedQueryHash } = useDevtoolsState()

const theme = useTheme()
const css = useQueryDevtoolsContext().shadowDOMTarget
? goober.css.bind({ target: useQueryDevtoolsContext().shadowDOMTarget })
Expand Down Expand Up @@ -1485,6 +1478,8 @@ const QueryRow: Component<{ query: Query }> = (props) => {
}

const MutationRow: Component<{ mutation: Mutation }> = (props) => {
const { selectedMutationId, setSelectedMutationId } = useDevtoolsState()

const theme = useTheme()
const css = useQueryDevtoolsContext().shadowDOMTarget
? goober.css.bind({ target: useQueryDevtoolsContext().shadowDOMTarget })
Expand Down Expand Up @@ -1726,6 +1721,8 @@ const MutationStatusCount: Component = () => {
}

const QueryStatus: Component<QueryStatusProps> = (props) => {
const { selectedQueryHash, panelWidth } = useDevtoolsState()

const theme = useTheme()
const css = useQueryDevtoolsContext().shadowDOMTarget
? goober.css.bind({ target: useQueryDevtoolsContext().shadowDOMTarget })
Expand Down Expand Up @@ -1841,6 +1838,8 @@ const QueryStatus: Component<QueryStatusProps> = (props) => {
}

const QueryDetails = () => {
const { selectedQueryHash, setSelectedQueryHash } = useDevtoolsState()

const theme = useTheme()
const css = useQueryDevtoolsContext().shadowDOMTarget
? goober.css.bind({ target: useQueryDevtoolsContext().shadowDOMTarget })
Expand Down Expand Up @@ -2386,6 +2385,8 @@ const QueryDetails = () => {
}

const MutationDetails = () => {
const { selectedMutationId } = useDevtoolsState()

const theme = useTheme()
const css = useQueryDevtoolsContext().shadowDOMTarget
? goober.css.bind({ target: useQueryDevtoolsContext().shadowDOMTarget })
Expand Down Expand Up @@ -2573,42 +2574,30 @@ const MutationDetails = () => {
)
}

const queryCacheMap = new Map<
(q: Accessor<QueryCache>) => any,
{
setter: Setter<any>
shouldUpdate: (event: QueryCacheNotifyEvent) => boolean
}
>()

const setupQueryCacheSubscription = () => {
const { queryCacheSubscriptions } = useDevtoolsSubscriptions()
const queryCache = createMemo(() => {
const client = useQueryDevtoolsContext().client
return client.getQueryCache()
})

const unsubscribe = queryCache().subscribe((q) => {
batch(() => {
for (const [callback, value] of queryCacheMap.entries()) {
if (!value.shouldUpdate(q)) continue
value.setter(callback(queryCache))
}
createEffect(() => {
const cache = queryCache()
const unsubscribe = cache.subscribe((event) => {
batch(() => {
queryCacheSubscriptions.notify(() => cache, event)
})
})
onCleanup(unsubscribe)
})

onCleanup(() => {
queryCacheMap.clear()
unsubscribe()
})

return unsubscribe
}

const createSubscribeToQueryCacheBatcher = <T,>(
callback: (queryCache: Accessor<QueryCache>) => Exclude<T, Function>,
equalityCheck: boolean = true,
shouldUpdate: (event: QueryCacheNotifyEvent) => boolean = () => true,
) => {
const { queryCacheSubscriptions } = useDevtoolsSubscriptions()
const queryCache = createMemo(() => {
const client = useQueryDevtoolsContext().client
return client.getQueryCache()
Expand All @@ -2623,49 +2612,44 @@ const createSubscribeToQueryCacheBatcher = <T,>(
setValue(callback(queryCache))
})

queryCacheMap.set(callback, {
setter: setValue,
shouldUpdate: shouldUpdate,
})

onCleanup(() => {
queryCacheMap.delete(callback)
})
queryCacheSubscriptions.register(callback, setValue, shouldUpdate)

return value
}

const mutationCacheMap = new Map<
(q: Accessor<MutationCache>) => any,
Setter<any>
>()

const setupMutationCacheSubscription = () => {
const { mutationCacheSubscriptions } = useDevtoolsSubscriptions()
const mutationCache = createMemo(() => {
const client = useQueryDevtoolsContext().client
return client.getMutationCache()
})

const unsubscribe = mutationCache().subscribe(() => {
for (const [callback, setter] of mutationCacheMap.entries()) {
queueMicrotask(() => {
setter(callback(mutationCache))
})
}
})

onCleanup(() => {
mutationCacheMap.clear()
unsubscribe()
createEffect(() => {
const cache = mutationCache()
let active = true
const unsubscribe = cache.subscribe((event) => {
mutationCacheSubscriptions.notify(
() => cache,
event,
(update) => {
queueMicrotask(() => {
if (active) update()
})
},
)
})
onCleanup(() => {
active = false
unsubscribe()
})
})

return unsubscribe
}

const createSubscribeToMutationCacheBatcher = <T,>(
callback: (queryCache: Accessor<MutationCache>) => Exclude<T, Function>,
equalityCheck: boolean = true,
) => {
const { mutationCacheSubscriptions } = useDevtoolsSubscriptions()
const mutationCache = createMemo(() => {
const client = useQueryDevtoolsContext().client
return client.getMutationCache()
Expand All @@ -2680,11 +2664,7 @@ const createSubscribeToMutationCacheBatcher = <T,>(
setValue(callback(mutationCache))
})

mutationCacheMap.set(callback, setValue)

onCleanup(() => {
mutationCacheMap.delete(callback)
})
mutationCacheSubscriptions.register(callback, setValue)

return value
}
Expand Down
22 changes: 17 additions & 5 deletions packages/query-devtools/src/DevtoolsComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { createLocalStorage } from '@solid-primitives/storage'
import { createMemo } from 'solid-js'
import { DevtoolsOfflineProvider } from './providers/DevtoolsOfflineProvider'
import { DevtoolsSubscriptionsProvider } from './providers/DevtoolsSubscriptionsProvider'
import { DevtoolsStateProvider } from './providers/DevtoolsStateProvider'
import { Devtools } from './Devtools'
import { getPreferredColorScheme } from './utils'
import { THEME_PREFERENCE } from './constants'
Expand All @@ -24,11 +27,20 @@ const DevtoolsComponent: DevtoolsComponentType = (props) => {

return (
<QueryDevtoolsContext.Provider value={props}>
<PiPProvider localStore={localStore} setLocalStore={setLocalStore}>
<ThemeContext.Provider value={theme}>
<Devtools localStore={localStore} setLocalStore={setLocalStore} />
</ThemeContext.Provider>
</PiPProvider>
<DevtoolsStateProvider>
<DevtoolsOfflineProvider>
<DevtoolsSubscriptionsProvider>
<PiPProvider localStore={localStore} setLocalStore={setLocalStore}>
<ThemeContext.Provider value={theme}>
<Devtools
localStore={localStore}
setLocalStore={setLocalStore}
/>
</ThemeContext.Provider>
</PiPProvider>
</DevtoolsSubscriptionsProvider>
</DevtoolsOfflineProvider>
</DevtoolsStateProvider>
</QueryDevtoolsContext.Provider>
)
}
Expand Down
37 changes: 23 additions & 14 deletions packages/query-devtools/src/DevtoolsPanelComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { createLocalStorage } from '@solid-primitives/storage'
import { createMemo } from 'solid-js'
import { DevtoolsOfflineProvider } from './providers/DevtoolsOfflineProvider'
import { DevtoolsSubscriptionsProvider } from './providers/DevtoolsSubscriptionsProvider'
import { DevtoolsStateProvider } from './providers/DevtoolsStateProvider'
import { ContentView, ParentPanel } from './Devtools'
import { getPreferredColorScheme } from './utils'
import { THEME_PREFERENCE } from './constants'
Expand All @@ -24,22 +27,28 @@ const DevtoolsPanelComponent: DevtoolsComponentType = (props) => {

return (
<QueryDevtoolsContext.Provider value={props}>
<PiPProvider
disabled
localStore={localStore}
setLocalStore={setLocalStore}
>
<ThemeContext.Provider value={theme}>
<ParentPanel>
<ContentView
<DevtoolsStateProvider>
<DevtoolsOfflineProvider>
<DevtoolsSubscriptionsProvider>
<PiPProvider
disabled
localStore={localStore}
setLocalStore={setLocalStore}
onClose={props.onClose}
showPanelViewOnly
/>
</ParentPanel>
</ThemeContext.Provider>
</PiPProvider>
>
<ThemeContext.Provider value={theme}>
<ParentPanel>
<ContentView
localStore={localStore}
setLocalStore={setLocalStore}
onClose={props.onClose}
showPanelViewOnly
/>
</ParentPanel>
</ThemeContext.Provider>
</PiPProvider>
</DevtoolsSubscriptionsProvider>
</DevtoolsOfflineProvider>
</DevtoolsStateProvider>
</QueryDevtoolsContext.Provider>
)
}
Expand Down
Loading
Loading