diff --git a/.changeset/fair-donuts-chew.md b/.changeset/fair-donuts-chew.md new file mode 100644 index 000000000..6b6906449 --- /dev/null +++ b/.changeset/fair-donuts-chew.md @@ -0,0 +1,6 @@ +--- +'@tanstack/react-pacer': patch +'@tanstack/preact-pacer': patch +--- + +Use the latest `onUnmount` callback during cleanup so adapter teardown reflects current options instead of the initial render. diff --git a/packages/preact-pacer/src/debouncer/useDebouncer.ts b/packages/preact-pacer/src/debouncer/useDebouncer.ts index 198ff79df..a3ab3c1ee 100644 --- a/packages/preact-pacer/src/debouncer/useDebouncer.ts +++ b/packages/preact-pacer/src/debouncer/useDebouncer.ts @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from 'preact/hooks' +import { useEffect, useMemo, useRef, useState } from 'preact/hooks' import { Debouncer } from '@tanstack/pacer/debouncer' import { shallow, useSelector } from '@tanstack/preact-store' import { useDefaultPacerOptions } from '../provider/PacerProvider' @@ -193,12 +193,14 @@ export function useDebouncer( debouncer.fn = fn debouncer.setOptions(mergedOptions) + const onUnmountRef = useRef(mergedOptions.onUnmount) + onUnmountRef.current = mergedOptions.onUnmount /* eslint-disable react-hooks/exhaustive-deps -- cleanup only; runs on unmount */ useEffect(() => { return () => { - if (mergedOptions.onUnmount) { - mergedOptions.onUnmount(debouncer) + if (onUnmountRef.current) { + onUnmountRef.current(debouncer) } else { debouncer.cancel() } diff --git a/packages/react-pacer/src/async-debouncer/useAsyncDebouncer.ts b/packages/react-pacer/src/async-debouncer/useAsyncDebouncer.ts index 795b89acf..9d3947de5 100644 --- a/packages/react-pacer/src/async-debouncer/useAsyncDebouncer.ts +++ b/packages/react-pacer/src/async-debouncer/useAsyncDebouncer.ts @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { AsyncDebouncer } from '@tanstack/pacer/async-debouncer' import { shallow, useSelector } from '@tanstack/react-store' import { useDefaultPacerOptions } from '../provider/PacerProvider' @@ -248,6 +248,8 @@ export function useAsyncDebouncer( asyncDebouncer.fn = fn asyncDebouncer.setOptions(mergedOptions) + const onUnmountRef = useRef(mergedOptions.onUnmount) + onUnmountRef.current = mergedOptions.onUnmount const state = useSelector(asyncDebouncer.store, selector, { compare: shallow, @@ -256,8 +258,8 @@ export function useAsyncDebouncer( /* eslint-disable react-hooks/exhaustive-deps, @eslint-react/exhaustive-deps, react-compiler/react-compiler -- unmount cleanup only; empty deps keep teardown stable */ useEffect(() => { return () => { - if (mergedOptions.onUnmount) { - mergedOptions.onUnmount(asyncDebouncer) + if (onUnmountRef.current) { + onUnmountRef.current(asyncDebouncer) } else { asyncDebouncer.cancel() asyncDebouncer.abort() diff --git a/packages/react-pacer/src/debouncer/useDebouncer.ts b/packages/react-pacer/src/debouncer/useDebouncer.ts index 903d7e351..ea75c4b05 100644 --- a/packages/react-pacer/src/debouncer/useDebouncer.ts +++ b/packages/react-pacer/src/debouncer/useDebouncer.ts @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { Debouncer } from '@tanstack/pacer/debouncer' import { shallow, useSelector } from '@tanstack/react-store' import { useDefaultPacerOptions } from '../provider/PacerProvider' @@ -194,12 +194,14 @@ export function useDebouncer( debouncer.fn = fn debouncer.setOptions(mergedOptions) + const onUnmountRef = useRef(mergedOptions.onUnmount) + onUnmountRef.current = mergedOptions.onUnmount /* eslint-disable react-hooks/exhaustive-deps, @eslint-react/exhaustive-deps, react-compiler/react-compiler -- unmount cleanup only; empty deps keep teardown stable */ useEffect(() => { return () => { - if (mergedOptions.onUnmount) { - mergedOptions.onUnmount(debouncer) + if (onUnmountRef.current) { + onUnmountRef.current(debouncer) } else { debouncer.cancel() } diff --git a/packages/react-pacer/src/rate-limiter/useRateLimiter.ts b/packages/react-pacer/src/rate-limiter/useRateLimiter.ts index 090656eaa..0259f85ec 100644 --- a/packages/react-pacer/src/rate-limiter/useRateLimiter.ts +++ b/packages/react-pacer/src/rate-limiter/useRateLimiter.ts @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { RateLimiter } from '@tanstack/pacer/rate-limiter' import { shallow, useSelector } from '@tanstack/react-store' import { useDefaultPacerOptions } from '../provider/PacerProvider' @@ -221,12 +221,14 @@ export function useRateLimiter( rateLimiter.fn = fn rateLimiter.setOptions(mergedOptions) + const onUnmountRef = useRef(mergedOptions.onUnmount) + onUnmountRef.current = mergedOptions.onUnmount /* eslint-disable react-hooks/exhaustive-deps, @eslint-react/exhaustive-deps, react-compiler/react-compiler -- unmount cleanup only; empty deps keep teardown stable */ useEffect(() => { return () => { - if (mergedOptions.onUnmount) { - mergedOptions.onUnmount(rateLimiter) + if (onUnmountRef.current) { + onUnmountRef.current(rateLimiter) } } }, [])