Hone logo
Hone
Problems

Implementing useDeferredValue in React

React's useDeferredValue hook allows you to defer updating a part of the UI until the browser has had a chance to paint. This is particularly useful when dealing with computationally expensive updates or frequent re-renders triggered by user input, preventing the UI from feeling sluggish. Your task is to implement a simplified version of useDeferredValue that demonstrates the core functionality of deferring a value.

Problem Description

You need to implement a custom hook called useDeferredValue that accepts a value and a deferred callback function. The hook should maintain a deferred version of the value. The deferred value should only update when the browser is idle. The deferred callback should be invoked with the deferred value when it changes. The hook should return an object containing both the original value and the deferred value.

Key Requirements:

  • Deferred Update: The deferred value should not update immediately when the original value changes. It should wait until the browser is idle.
  • Callback Execution: The deferred callback function should be invoked with the deferred value whenever the deferred value changes.
  • React Integration: The hook should be a valid React hook and follow React's rules of hooks.
  • Idling: Use window.requestIdleCallback to schedule the deferred update. If requestIdleCallback is not supported, fall back to setTimeout with a short delay (e.g., 0ms).

Expected Behavior:

  1. When the original value changes, the hook should schedule an update of the deferred value using requestIdleCallback (or setTimeout).
  2. The deferred value should only update after the browser is idle (or after the short timeout if requestIdleCallback is not supported).
  3. The deferred callback function should be called with the updated deferred value.
  4. The hook should return an object with two properties: value (the original value) and deferredValue (the deferred value).

Edge Cases to Consider:

  • Browser does not support requestIdleCallback.
  • Component unmounts before the idle callback is executed. (Handle this gracefully to avoid memory leaks).
  • Rapid changes to the original value. The hook should queue updates and only execute the deferred update when the browser is idle.

Examples

Example 1:

Input:
Original Value: "Hello"
Deferred Callback: (deferredValue) => console.log("Deferred Value:", deferredValue)

Output:
Initially, deferredValue is undefined.
After a short delay (or when the browser is idle), deferredValue becomes "Hello" and the callback logs "Deferred Value: Hello".

Explanation: The original value changes to "Hello". The hook schedules an update. After the browser is idle, the deferred value is updated to "Hello", and the callback is executed.

Example 2:

Input:
Original Value: "Hello"
Deferred Callback: (deferredValue) => console.log("Deferred Value:", deferredValue)
Original Value changes to "World" immediately after the first update.

Output:
Initially, deferredValue is undefined.
After a short delay (or when the browser is idle), deferredValue becomes "Hello" and the callback logs "Deferred Value: Hello".
Then, after another short delay (or when the browser is idle), deferredValue becomes "World" and the callback logs "Deferred Value: World".

Explanation: Multiple updates are queued. The deferred value updates sequentially when the browser is idle.

Example 3: (Edge Case - Component Unmount)

Input:
Original Value: "Initial"
Deferred Callback: (deferredValue) => console.log("Deferred Value:", deferredValue)
The component unmounts before the idle callback is executed.

Output:
No error is thrown. The callback is not executed.

Explanation: The component unmounts before the deferred update can occur. The hook should prevent the callback from being executed in this scenario.

Constraints

  • The hook must be written in TypeScript.
  • The hook must use requestIdleCallback for deferred updates, falling back to setTimeout if requestIdleCallback is not supported.
  • The deferred callback should only be invoked once for each unique deferred value.
  • The hook should not introduce any memory leaks.
  • The hook should be performant and avoid unnecessary re-renders.

Notes

  • Consider using useRef to store the latest original value and the deferred value to avoid unnecessary re-renders.
  • Be mindful of the component's lifecycle and handle unmounting gracefully.
  • The requestIdleCallback API provides a deadline object that can be used to limit the amount of work done in the idle callback. While not strictly required for this simplified implementation, it's a good practice to consider for more complex scenarios.
  • Focus on the core functionality of deferring updates and invoking the callback when the deferred value changes. Error handling and more advanced features are not required for this challenge.
Loading editor...
typescript