Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] [Experiment] Refactor Subscribable and NotifyManager to TanStack Store #6799

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/query-core/package.json
Expand Up @@ -44,5 +44,8 @@
"test:lib:dev": "pnpm run test:lib --watch",
"test:build": "publint --strict",
"build": "tsup"
},
"dependencies": {
"@tanstack/store": "0.3.1"
}
}
82 changes: 33 additions & 49 deletions packages/query-core/src/focusManager.ts
@@ -1,77 +1,61 @@
import { Subscribable } from './subscribable'
import { Store } from '@tanstack/store'
import { isServer } from './utils'

type SetupFn = (
setFocused: (focused?: boolean) => void,
) => (() => void) | undefined

export class FocusManager extends Subscribable {
#focused?: boolean
#cleanup?: () => void

#setup: SetupFn
export class FocusManager {
store = new Store<boolean | undefined>(undefined, {
onSubscribe: () => {
if (!this.#cleanup) this.setEventListener(this.#setup)

constructor() {
super()
this.#setup = (onFocus) => {
// addEventListener does not exist in React Native, but window does
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!isServer && window.addEventListener) {
const listener = () => onFocus()
// Listen to visibilitychange
window.addEventListener('visibilitychange', listener, false)

return () => {
// Be sure to unsubscribe if a new handler is set
window.removeEventListener('visibilitychange', listener)
return () => {
if (!this.store.listeners.size) {
this.#cleanup?.()
this.#cleanup = undefined;
}
}
return
}
}
},
})

protected onSubscribe(): void {
if (!this.#cleanup) {
this.setEventListener(this.#setup)
}
}
#cleanup?: () => void

#setup: SetupFn = (onFocus) => {
// addEventListener does not exist in React Native, but window does
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!isServer && window.addEventListener) {
const listener = () => onFocus()
// Listen to visibilitychange
window.addEventListener('visibilitychange', listener, false)

protected onUnsubscribe() {
if (!this.hasListeners()) {
this.#cleanup?.()
this.#cleanup = undefined
return () => {
// Be sure to unsubscribe if a new handler is set
window.removeEventListener('visibilitychange', listener)
}
}
return
}

setEventListener(setup: SetupFn): void {
this.#setup = setup
this.#cleanup?.()
this.#cleanup = setup((focused) => {
if (typeof focused === 'boolean') {
this.setFocused(focused)
} else {
this.onFocus()
}
})
this.#cleanup = setup(this.setFocused.bind(this))
}

setFocused(focused?: boolean): void {
const changed = this.#focused !== focused
if (changed) {
this.#focused = focused
this.onFocus()
}
const changed = this.store.state !== focused;
if (!changed) return;
this.store.setState(() => focused)
}

onFocus(): void {
this.listeners.forEach((listener) => {
listener()
})
subscribe(fn: () => void) {
return this.store.subscribe(fn);
}

isFocused(): boolean {
if (typeof this.#focused === 'boolean') {
return this.#focused
if (typeof this.store.state === 'boolean') {
return this.store.state
}

// document global can be unavailable in react native
Expand Down
77 changes: 35 additions & 42 deletions packages/query-core/src/onlineManager.ts
@@ -1,49 +1,42 @@
import { Subscribable } from './subscribable'
import { Store } from '@tanstack/store'
import { isServer } from './utils'

type Listener = (online: boolean) => void
type SetupFn = (setOnline: Listener) => (() => void) | undefined

export class OnlineManager extends Subscribable<Listener> {
#online = true
#cleanup?: () => void

#setup: SetupFn

constructor() {
super()
this.#setup = (onOnline) => {
// addEventListener does not exist in React Native, but window does
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!isServer && window.addEventListener) {
const onlineListener = () => onOnline(true)
const offlineListener = () => onOnline(false)
// Listen to online
window.addEventListener('online', onlineListener, false)
window.addEventListener('offline', offlineListener, false)
export class OnlineManager {
store = new Store(true, {
onSubscribe: () => {
if (!this.#cleanup) this.setEventListener(this.#setup)

return () => {
// Be sure to unsubscribe if a new handler is set
window.removeEventListener('online', onlineListener)
window.removeEventListener('offline', offlineListener)
return () => {
if (!this.store.listeners.size) {
this.#cleanup?.()
this.#cleanup = undefined
}
}
},
})
#cleanup?: () => void

return
}
}
#setup: SetupFn = (onOnline) => {
// addEventListener does not exist in React Native, but window does
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!isServer && window.addEventListener) {
const onlineListener = () => onOnline(true)
const offlineListener = () => onOnline(false)
// Listen to online
window.addEventListener('online', onlineListener, false)
window.addEventListener('offline', offlineListener, false)

protected onSubscribe(): void {
if (!this.#cleanup) {
this.setEventListener(this.#setup)
return () => {
// Be sure to unsubscribe if a new handler is set
window.removeEventListener('online', onlineListener)
window.removeEventListener('offline', offlineListener)
}
}
}

protected onUnsubscribe() {
if (!this.hasListeners()) {
this.#cleanup?.()
this.#cleanup = undefined
}
return
}

setEventListener(setup: SetupFn): void {
Expand All @@ -53,18 +46,18 @@ export class OnlineManager extends Subscribable<Listener> {
}

setOnline(online: boolean): void {
const changed = this.#online !== online
const changed = this.store.state !== online

if (changed) {
this.#online = online
this.listeners.forEach((listener) => {
listener(online)
})
}
if (!changed) return
this.store.setState(() => online)
}

subscribe(fn: (state: boolean) => void) {
return this.store.subscribe(() => fn(this.store.state))
}

isOnline(): boolean {
return this.#online
return this.store.state
}
}

Expand Down