# Performance

Whenever the `timeframe` changes, a recalculation of all the item's width and position must be performed.

When performing operations that require a high amount of sequential changes, like zooming in or panning, the resulted re-renders might cause stuttering and frame-drops.

This **"frequent change"** problem is quite common in reactive frontend applications, and there are multiple solutions to it.

As you are in full control of the `timeframe` state, you can apply a chosen mechanism of debouncing to the `timeframe` state.

This will reduce the amount changes registered in the timeline, and in return reduce the amount of renders required when zooming an panning, resulting in a great improvement in performance.

Here are some common solutions:

{% tabs %}
{% tab title="Deferring ⭐️" %}
If you use **React 18+**, you should make use of the [`useDeferredValue`](https://react.dev/reference/react/useDeferredValue) API.

> `useDeferredValue` is a React Hook that lets you defer updating a part of the UI.

#### Code Example

{% tabs %}
{% tab title="App.tsx" %}

<pre class="language-tsx"><code class="lang-tsx">function App() {
  const [timeframe, setTimeframe] = useState(DEFAULT_TIMEFRAME);
<strong>  const debouncedTimeframe = useDeferredValue(timeframe);
</strong>  
  ...
  
  return (
    &#x3C;TimelineContext
        onDragEnd={onDragEnd}
        onResizeEnd={onResizeEnd}
<strong>        timeframe={debouncedTimeframe} // provide the debounced timeframe
</strong>        onTimeframeChanged={setTimeframe}
    >
      &#x3C;Timeline items={items} rows={rows} />
    &#x3C;/TimelineContext>
  )
</code></pre>

{% endtab %}

{% tab title="Item.tsx" %}

<pre class="language-tsx"><code class="lang-tsx">function Item(props: ItemProps) {
  ...
  const style: CSSProperties = {
    ...itemStyle,
<strong>    transition: 'left .2s linear, width .2s linear', 
</strong><strong>    // You can a CSS transition to make the debounce feel smoother
</strong>  }

  return (
    &#x3C;div ref={setNodeRef} style={style} {...listeners} {...attributes}>
    ...
    &#x3C;/div>
  )
}
</code></pre>

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="Throttle" %}
Another common solution is **throttling.**

> Throttling limits the rate of function calls. It guarantees that a function is only executed once within a set time interval. If the function is called multiple times during that interval, only the first call is executed. Subsequent calls are ignored until the interval has elapsed.

#### Code Example

{% tabs %}
{% tab title="App.tsx" %}

<pre class="language-tsx"><code class="lang-tsx">function App() {
  const [timeframe, setTimeframe] = useState(DEFAULT_TIMEFRAME);
<strong>  const debouncedTimeframe = useThrottle(timeframe, 300);
</strong>  
  ...
  
  return (
    &#x3C;TimelineContext
        onDragEnd={onDragEnd}
        onResizeEnd={onResizeEnd}
<strong>        timeframe={debouncedTimeframe} // provide the debounced timeframe
</strong>        onTimeframeChanged={setTimeframe}
    >
      &#x3C;Timeline items={items} rows={rows} />
    &#x3C;/TimelineContext>
  )
</code></pre>

{% endtab %}

{% tab title="Item.tsx" %}

<pre class="language-tsx"><code class="lang-tsx">function Item(props: ItemProps) {
  ...
  const style: CSSProperties = {
    ...itemStyle,
<strong>    transition: 'left .2s linear, width .2s linear', 
</strong><strong>    // You can a CSS transition to make the debounce feel smoother
</strong>  }

  return (
    &#x3C;div ref={setNodeRef} style={style} {...listeners} {...attributes}>
    ...
    &#x3C;/div>
  )
}
</code></pre>

{% endtab %}

{% tab title="useThrottle.tsx" %}

```tsx
export function useThrottle<T>(value: T, interval = 500) {
  const [throttledValue, setThrottledValue] = useState(value);
  const lastUpdated = useRef<number | null>(null);

  useEffect(() => {
    const now = Date.now();

    if (lastUpdated.current && now >= lastUpdated.current + interval) {
      lastUpdated.current = now;
      setThrottledValue(value);
    } else {
      const id = window.setTimeout(() => {
        lastUpdated.current = now;
        setThrottledValue(value);
      }, interval);

      return () => {
        window.clearTimeout(id)
      };
    }
  }, [value, interval]);

  return throttledValue;
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="Debounce" %}
The most common way to solve this is using **debounce.**

> Debouncing prevents extra activations or slow functions from triggering too often. It will wait for a given interval to elapse since the last change, and only then apply it.

#### Code Example

{% tabs %}
{% tab title="App.tsx" %}

<pre class="language-tsx"><code class="lang-tsx">function App() {
  const [timeframe, setTimeframe] = useState(DEFAULT_TIMEFRAME);
<strong>  const debouncedTimeframe = useDebounce(timeframe, 300)
</strong>  
  ...
  
  return (
    &#x3C;TimelineContext
        onDragEnd={onDragEnd}
        onResizeEnd={onResizeEnd}
<strong>        timeframe={debouncedTimeframe} // provide the debounced timeframe
</strong>        onTimeframeChanged={setTimeframe}
    >
      &#x3C;Timeline items={items} rows={rows} />
    &#x3C;/TimelineContext>
  )
</code></pre>

{% endtab %}

{% tab title="Item.tsx" %}

<pre class="language-tsx"><code class="lang-tsx">function Item(props: ItemProps) {
  ...
  const style: CSSProperties = {
    ...itemStyle,
<strong>    transition: 'left .2s linear, width .2s linear', 
</strong><strong>    // You can a CSS transition to make the debounce feel smoother
</strong>  }

  return (
    &#x3C;div ref={setNodeRef} style={style} {...listeners} {...attributes}>
    ...
    &#x3C;/div>
  )
}
</code></pre>

{% endtab %}

{% tab title="useDebounce.tsx" %}

```tsx
export function useDebounce<T>(value: T, delay = 500) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}
```

{% endtab %}
{% endtabs %}
{% endtab %}
{% endtabs %}

> 🧠 You can still make use of the un-debounced state to render selected components in real-time. For example, you can render the time-axis using the un-debounced state, and render the timeline using the debounced state.
>
> Play around with the live demo, and watch how the timeaxis and the timeframe move asynchronously🔥

### Live Example

In this example, You can play around with difference modes and feel the difference between them.

[Click here to see the full code](https://github.com/samuelarbibe/dnd-timeline/blob/main/docs/examples/performance/src/App.tsx)

{% embed url="<https://dnd-timeline-performance.vercel.app>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://samuel-arbibe.gitbook.io/dnd-timeline/1/documentation/zoom-and-pan/performance.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
