# UseCallback

*The `UseCallback` [hook](../01_RulesOfHooks.md) memoizes callback functions, preventing unnecessary re-renders when callbacks are passed as props to [child components](../../../01_Onboarding/02_Concepts/03_Widgets.md) or used as dependencies in other [hooks](../01_RulesOfHooks.md).*

## Overview

The `UseCallback` [hook](../01_RulesOfHooks.md) provides a way to optimize callback functions in Ivy [applications](../../../01_Onboarding/02_Concepts/10_Apps.md):

- **Stable Function References** - Returns the same function reference when [state](./03_UseState.md) dependencies haven't changed
- **Prevents Re-renders** - [Child components](../../../01_Onboarding/02_Concepts/03_Widgets.md) won't re-render unnecessarily when receiving memoized callbacks
- **Stable Dependencies** - Ensures callbacks used in [`UseEffect`](./04_UseEffect.md) and other hooks have stable references

> **Tip:** `UseCallback` memoizes the function reference itself, while [`UseMemo`](./05_UseMemo.md) memoizes the result of calling a function. The memoized callback is only executed when you invoke it.

## Basic Usage

```csharp
public class ParentView : ViewBase
{
    public override object? Build()
    {
        var count = UseState(0);
        var multiplier = UseState(2);
        
        // Memoize the callback to prevent child re-renders
        var handleIncrement = UseCallback(() => 
        {
            count.Set(count.Value + 1);
        }, count); // Only recreate when count changes
        
        var handleReset = UseCallback(() => 
        {
            count.Set(0);
        }); // No dependencies - callback never changes
        
        return Layout.Vertical(
            Text.Inline($"Count: {count.Value}"),
            new ChildComponent(handleIncrement, handleReset),
            new NumberInput("Multiplier", multiplier.Value, v => multiplier.Set(v))
        );
    }
}
```

## When to Use UseCallback

```mermaid
flowchart TD
    A["Need to optimize callbacks?"] --> B{What's the use case?}
    
    B --> C["Callback passed to<br/>child component"]
    B --> D["Callback used as<br/>dependency in UseEffect"]
    B --> E["Event handler with<br/>expensive setup"]
    B --> F["Callback in list items<br/>or repeated components"]
    
    C --> G["Use UseCallback<br/>Prevents child re-renders"]
    D --> H["Use UseCallback<br/>Prevents infinite loops"]
    E --> I["Use UseCallback<br/>Avoids recreation overhead"]
    F --> J["Use UseCallback<br/>Optimizes list performance"]
    
    G --> K["Stable function reference<br/> Child won't re-render<br/> Better performance"]
    H --> L["Stable dependency<br/> Effect runs correctly<br/> No infinite loops"]
    I --> M[" Handler created once<br/> Less memory churn<br/> Faster renders"]
    J --> N[" List items optimized<br/> Better scrolling<br/> Smoother UI"]
```

The `UseCallback` [hook](../01_RulesOfHooks.md) memoizes callback functions and only recreates them when their [state](./03_UseState.md) dependencies change.

> **Tip:** `UseCallback` hook stores only the most recent dependency values for comparison; older values are discarded.

### How UseCallback Works

```mermaid
sequenceDiagram
    participant C as Component
    participant CB as UseCallback Hook
    participant S as UseState Storage
    
    Note over C,S: First Render
    C->>CB: UseCallback(() => handleClick(), [dep1, dep2])
    CB->>S: UseState(() => new CallbackRef(callback, deps))
    S-->>CB: Create new CallbackRef with callback
    CB->>S: Store CallbackRef(callback, [dep1, dep2])
    CB-->>C: Return callback function
    
    Note over C,S: Subsequent Render (deps unchanged)
    C->>CB: UseCallback(() => handleClick(), [dep1, dep2])
    CB->>S: Get stored CallbackRef
    S-->>CB: Return CallbackRef(cachedCallback, [dep1, dep2])
    CB->>CB: AreDependenciesEqual([dep1, dep2], [dep1, dep2])
    Note right of CB: Dependencies equal!<br/>Return same function reference
    CB-->>C: Return cached callback (same reference)
    
    Note over C,S: Subsequent Render (deps changed)
    C->>CB: UseCallback(() => handleClick(), [dep1_new, dep2])
    CB->>S: Get stored CallbackRef
    S-->>CB: Return CallbackRef(oldCallback, [dep1, dep2])
    CB->>CB: AreDependenciesEqual([dep1, dep2], [dep1_new, dep2])
    Note right of CB: Dependencies changed!<br/>Create new function reference
    CB->>S: Update CallbackRef(newCallback, [dep1_new, dep2])
    CB-->>C: Return new callback function
```

### Memory vs Speed Trade-offs

- **Function References**: `UseCallback` stores function references in memory. Consider the number of memoized callbacks:

```csharp
// Good: Small number of memoized callbacks
var handleClick = UseCallback(() => DoSomething(), []);
var handleSubmit = UseCallback(() => SubmitForm(), formData);

// Caution: Many memoized callbacks might consume memory
// Consider if all are necessary
```

- **[State](./03_UseState.md) Dependency Stability**: If state dependencies change frequently, callbacks will be recreated often, reducing the effectiveness of memoization:

```csharp
// Bad: Dependency changes on every render
var config = new { threshold: 100 };
var handleAction = UseCallback(() => DoAction(config), config);

// Good: Stable dependency
var threshold = UseState(100);
var handleAction = UseCallback(() => DoAction(threshold.Value), threshold);
```

- **Callback Complexity**: Simple callbacks may not benefit from memoization:

```csharp
// Unnecessary memoization for simple callback
var handleClick = UseCallback(() => count.Set(count.Value + 1), count);

// Consider direct inline for simple cases
// Or memoize only if passed to many child components
```

## Best Practices

- **Dependency Array**: Always specify the [state](./03_UseState.md) dependencies that should trigger callback recreation
- **Stable References**: Only include state values that actually affect the callback's behavior
- **Avoid Over-Memoization**: Don't memoize simple callbacks that don't cause performance issues
- **Combine with IMemoized**: Use `UseCallback` together with `IMemoized` [components](../../../01_Onboarding/02_Concepts/02_Views.md) for maximum optimization

## See Also

- [Memoization](./05_UseMemo.md) - Caching computed values with UseMemo
- [UseMemo](./05_UseMemo.md) - Memoizing function resultss
- [Effects](./04_UseEffect.md) - Performing side effects with stable dependencies
- [State Management](./03_UseState.md) - Managing component state
- [Rules of Hooks](../02_RulesOfHooks.md) - Understanding hook rules and best practices
- [UseRef](./08_UseRef.md) - Storing stable references
- [Views](../../../01_Onboarding/02_Concepts/02_Views.md) - Understanding Ivy views and components
- [Widgets](../../../01_Onboarding/02_Concepts/03_Widgets.md) - Building UI components

## Examples


### Preventing Child Re-renders

```csharp
public class TodoListView : ViewBase
{
    public override object? Build()
    {
        var todos = UseState(new List<Todo>());
        var filter = UseState("");
        
        // Memoize callbacks to prevent TodoItem re-renders
        var handleToggle = UseCallback((int id) => 
        {
            todos.Set(todos.Value.Select(t => 
                t.Id == id ? t with { Completed = !t.Completed } : t
            ).ToList());
        }, todos);
        
        var handleDelete = UseCallback((int id) => 
        {
            todos.Set(todos.Value.Where(t => t.Id != id).ToList());
        }, todos);
        
        var filteredTodos = UseMemo(() => 
            todos.Value.Where(t => 
                t.Title.Contains(filter.Value, StringComparison.OrdinalIgnoreCase)
            ).ToList(),
            todos, filter
        );
        
        return Layout.Vertical(
            new TextInput("Filter", filter.Value, v => filter.Set(v)),
            Layout.Vertical(
                filteredTodos.Select(todo => 
                    new TodoItem(todo, handleToggle, handleDelete).Key(todo.Id)
                )
            )
        );
    }
}
```




### Stable Dependencies for Effects

```csharp
public class DataFetcherView : ViewBase
{
    public override object? Build()
    {
        var data = UseState<List<Item>?>(null);
        var loading = UseState(false);
        var searchTerm = UseState("");
        
        // Memoize the fetch function
        var fetchData = UseCallback(async () => 
        {
            loading.Set(true);
            try
            {
                var result = await ApiService.SearchItems(searchTerm.Value);
                data.Set(result);
            }
            finally
            {
                loading.Set(false);
            }
        }, searchTerm);
        
        // Use the memoized callback in an effect
        UseEffect(async () => 
        {
            await fetchData();
        }, fetchData); // Stable dependency prevents infinite loops
        
        return Layout.Vertical(
            new TextInput("Search", searchTerm.Value, v => searchTerm.Set(v)),
            loading.Value ? new Loading() : new ItemList(data.Value ?? new List<Item>())
        );
    }
}
```