timeouts: rework effectschedule timeout tracking (#3870)

talked to @ds300 and decided that we needed to rework this latest bit to
cleanup outside of `EffectScheduler`

### Change Type

<!--  Please select a 'Scope' label ️ -->

- [x] `sdk` — Changes the tldraw SDK
- [ ] `dotcom` — Changes the tldraw.com web app
- [ ] `docs` — Changes to the documentation, examples, or templates.
- [ ] `vs code` — Changes to the vscode plugin
- [ ] `internal` — Does not affect user-facing stuff

<!--  Please select a 'Type' label ️ -->

- [x] `bugfix` — Bug fix
- [ ] `feature` — New feature
- [ ] `improvement` — Improving existing features
- [ ] `chore` — Updating dependencies, other boring stuff
- [ ] `galaxy brain` — Architectural changes
- [ ] `tests` — Changes to any test code
- [ ] `tools` — Changes to infrastructure, CI, internal scripts,
debugging tools, etc.
- [ ] `dunno` — I don't know
This commit is contained in:
Mime Čuvalo 2024-06-04 11:55:22 +01:00 committed by GitHub
parent aadc0aab4d
commit e74d2470c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 14 additions and 8 deletions

View file

@ -35,7 +35,7 @@ interface EffectSchedulerOptions {
* @param execute - A function that will execute the effect. * @param execute - A function that will execute the effect.
* @returns * @returns
*/ */
scheduleEffect?: (execute: () => void) => number | void | (() => void) scheduleEffect?: (execute: () => void) => void
} }
class __EffectScheduler__<Result> { class __EffectScheduler__<Result> {
@ -63,15 +63,13 @@ class __EffectScheduler__<Result> {
return this._scheduleCount return this._scheduleCount
} }
/** @internal */
private maybeRaf: number | void | (() => void) = -1
/** @internal */ /** @internal */
readonly parentSet = new ArraySet<Signal<any, any>>() readonly parentSet = new ArraySet<Signal<any, any>>()
/** @internal */ /** @internal */
readonly parentEpochs: number[] = [] readonly parentEpochs: number[] = []
/** @internal */ /** @internal */
readonly parents: Signal<any, any>[] = [] readonly parents: Signal<any, any>[] = []
private readonly _scheduleEffect?: (execute: () => void) => number | void | (() => void) private readonly _scheduleEffect?: (execute: () => void) => void
constructor( constructor(
public readonly name: string, public readonly name: string,
private readonly runEffect: (lastReactedEpoch: number) => Result, private readonly runEffect: (lastReactedEpoch: number) => Result,
@ -101,7 +99,7 @@ class __EffectScheduler__<Result> {
this._scheduleCount++ this._scheduleCount++
if (this._scheduleEffect) { if (this._scheduleEffect) {
// if the effect should be deferred (e.g. until a react render), do so // if the effect should be deferred (e.g. until a react render), do so
this.maybeRaf = this._scheduleEffect(this.maybeExecute) this._scheduleEffect(this.maybeExecute)
} else { } else {
// otherwise execute right now! // otherwise execute right now!
this.execute() this.execute()
@ -137,7 +135,6 @@ class __EffectScheduler__<Result> {
for (let i = 0, n = this.parents.length; i < n; i++) { for (let i = 0, n = this.parents.length; i < n; i++) {
detach(this.parents[i], this) detach(this.parents[i], this)
} }
typeof this.maybeRaf === 'number' && cancelAnimationFrame(this.maybeRaf)
} }
/** /**

View file

@ -1,10 +1,18 @@
import { useEffect, useMemo } from 'react' import { useEffect, useMemo, useRef } from 'react'
import { EffectScheduler } from '../core' import { EffectScheduler } from '../core'
/** @public */ /** @public */
export function useReactor(name: string, reactFn: () => void, deps: undefined | any[] = []) { export function useReactor(name: string, reactFn: () => void, deps: undefined | any[] = []) {
const raf = useRef(-1)
const scheduler = useMemo( const scheduler = useMemo(
() => new EffectScheduler(name, reactFn, { scheduleEffect: (cb) => requestAnimationFrame(cb) }), () =>
new EffectScheduler(name, reactFn, {
scheduleEffect: (cb) => {
const rafId = requestAnimationFrame(cb)
raf.current = rafId
return rafId
},
}),
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
deps deps
) )
@ -14,6 +22,7 @@ export function useReactor(name: string, reactFn: () => void, deps: undefined |
scheduler.execute() scheduler.execute()
return () => { return () => {
scheduler.detach() scheduler.detach()
cancelAnimationFrame(raf.current)
} }
}, [scheduler]) }, [scheduler])
} }