Created
January 4, 2025 06:23
-
-
Save hunghg255/c5295bc657703d5c976c76688968a12d to your computer and use it in GitHub Desktop.
Fix Interactive next paint INP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect, useLayoutEffect } from 'react'; | |
export function interactionResponse(): Promise<unknown> { | |
return new Promise((resolve) => { | |
setTimeout(resolve, 100); // Fallback for the case where the animation frame never fires. | |
requestAnimationFrame(() => { | |
setTimeout(resolve, 0); | |
}); | |
}); | |
} | |
/** | |
* Yields to main thread before continuing execution. | |
* If priority is 'user-blocking', it will asynchronously resolve in older browsers. | |
* @param {object} options - see [https://github.com/WICG/scheduling-apis/blob/main/explainers/yield-and-continuation.md](spec) | |
*/ | |
export function yieldToMain(options?: any) { | |
if ('scheduler' in window) { | |
// @ts-ignore | |
if ('yield' in scheduler) { | |
// @ts-ignore | |
return scheduler.yield(options); | |
} | |
// @ts-ignore | |
if ('postTask' in scheduler) { | |
// @ts-ignore | |
return scheduler.postTask(() => {}, options); | |
} | |
} | |
// `setTimeout` could suffer from being delayed for longer - so for browsers not supporting yield, | |
// we guarantee execution for high priority actions, but it doesn't yield as trade-off. | |
if (options?.priority === 'user-blocking') { | |
return Promise.resolve(); | |
} | |
return new Promise((resolve) => { | |
setTimeout(resolve, 100); // Fallback for the case where the animation frame never fires. | |
requestAnimationFrame(() => { | |
setTimeout(resolve, 0); | |
}); | |
}); | |
} | |
/** | |
* Returns a promise that resolves in the next frame. | |
*/ | |
const runAfterPaint = async (fn: any) => { | |
await yieldToMain({ | |
priority: 'background', | |
}); | |
return fn(); | |
}; | |
export function useAfterPaintEffect(useEffectFn: any, deps: any) { | |
useEffect(() => { | |
const runPromise = runAfterPaint(useEffectFn); | |
return () => { | |
(async () => { | |
const cleanup = await runPromise; | |
if (!cleanup) { | |
return; | |
} | |
runAfterPaint(cleanup); | |
})(); | |
}; | |
}, deps); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment