const { poll, release } = usePooledAd(poolConfig.poolId);
const showNext = useCallback(async () => {
const result = await poll();
if (result.status !== 'filled' || result.ad.format !== AdFormat.INTERSTITIAL) return;
const ad = release();
if (!ad || ad.format !== AdFormat.INTERSTITIAL || ad.isStaleByPolicy()) {
ad?.destroy();
return;
}
const off = ad.addAdEventListener(AdEventType.CLOSED, () => {
off();
ad.destroy();
});
// Just released and not destroyed, so show() can only reject here.
await ad.show().catch(() => {
off();
ad.destroy();
});
}, [poll, release]);
Poll-on-demand against a pool. Never polls during render.
This hook is state-first:
poll()updatesstatus,ad, anderror; do not stash the returned ad in parallel state. It owns a filled ad untilrelease():destroy()while the hook owns the ad leaves dead inventory in hook state, so release first when ownership must move.poolStatusanswers lookup/creation (absent,creating,ready,ready-degraded,error);statusanswers polling (polling,filled,empty,timeout,no-fill,error,stale-by-policy,consumed). These vocabularies are intentionally separate.consumedfires when a hook-owned pooled fullscreen ad that was shown emitsAdEventType.CLOSED; the hook then destroys it. That differs from the classic hook'sclosed, which destroys nothing.pollandreleasekeep stable callback identities while sampling the latestpoolId. Coalescing is per hook instance, not per pool: two placements polling the same depth-1 pool can starve each other.A hook-owned fullscreen ad's
show()keepsPooledAd.show()'s two error channels: the promise rejects when a show was already requested or the platform declines, and a destroyed ad (or invalid options with no show in flight) throws synchronously. Unlike the fullscreen hooks'show, it is not press-safe, so.catch()the promise and do not call it on a destroyed ad.