# UseRef
*Store values that persist across re-renders without triggering updates, similar to React's useRef for holding mutable values that don't affect the [view](../../../01_Onboarding/02_Concepts/02_Views.md) lifecycle.*
## Overview
Key characteristics of `UseRef`:
- **Non-Reactive Storage** - Values persist but don't trigger re-renders when changed
- **Mutable References** - Perfect for storing timers, subscriptions, and other mutable objects
- **Performance** - No dependency tracking or re-render overhead
- **Persistence** - Values survive across [component](../../../01_Onboarding/02_Concepts/02_Views.md) re-renders
> **Tip:** `UseRef` is ideal for storing mutable references that don't affect rendering, such as timers, subscriptions, DOM references, or previous [state](./03_UseState.md) values for comparison.
## Basic Usage
```csharp
public class BasicRefDemo : ViewBase
{
class Counter { public int Value = 0; }
public override object? Build()
{
var renderCount = this.UseRef(() => new Counter());
var forceUpdate = UseState(0);
// Increment without triggering re-render
renderCount.Value.Value++;
return Layout.Vertical()
| new Button("Force Re-render", _ => forceUpdate.Set(forceUpdate.Value + 1))
| Text.P($"This component has rendered {renderCount.Value.Value} times")
| Text.P("(Note: The count increments on each render, but doesn't trigger re-renders)").Small();
}
}
```
## How UseRef Works
```mermaid
sequenceDiagram
participant C as Component
participant UR as UseRef Hook
participant S as Storage
Note over C,S: First Render
C->>UR: UseRef(initialValue)
UR->>S: Check if value exists
S-->>UR: No value found
UR->>S: Store initialValue
S-->>UR: Return stored value container
UR-->>C: Return IState<T> container
Note over C,S: Subsequent Render
C->>UR: UseRef(initialValue)
UR->>S: Get stored value
S-->>UR: Return cached container
UR-->>C: Return same container
Note over C,S: Value Mutation (External)
C->>UR: Direct mutation (ref.Value = newValue)
Note right of C: Value changed but<br/>NO re-render triggered
UR->>S: Value updated in storage
Note right of UR: Component continues<br/>with same render
```
## When to Use UseRef
| Use UseRef For | Use Other Hooks Instead |
|----------------|-------------------------|
| Mutable references (timers, subscriptions) | Value affects UI → UseState |
| Tracking previous state values | Computed from other values → UseMemo |
| Caching expensive initializations | Needs to trigger side effects → UseState + UseEffect |
| Storing callback references | Simple constant → regular variable |
### UseRef vs UseState vs UseMemo
| Hook | Triggers Re-render | Mutable | Use Case |
|------|-------------------|---------|----------|
| [`UseState`](./03_UseState.md) | Yes | No | UI state that affects rendering |
| [`UseMemo`](./05_UseMemo.md) | No | No | Expensive calculations |
| `UseRef` | No | Yes | Mutable refs, timers, subscriptions |
## Performance Considerations
```csharp
// Good: Timer reference (no re-render needed)
var timerId = UseRef<Timer?>(null);
// Good: Previous value tracking
var previousCount = UseRef(0);
// Bad: Value affects UI - use UseState instead
var count = UseRef(0); // Won't trigger re-render!
// Bad: Computed value - use UseMemo instead
var total = UseRef(items.Sum()); // Won't update when items change!
```
## See Also
- [State Management](./03_UseState.md) - Reactive state with UseState
- [Rules of Hooks](../02_RulesOfHooks.md) - Understanding hook rules and best practices
- [Effects](./04_UseEffect.md) - Side effects and cleanup
- [Memoization](./05_UseMemo.md) - Performance optimization with UseMemo
- [Callbacks](./06_UseCallback.md) - Memoized callback functions with UseCallback
- [Views](../../../01_Onboarding/02_Concepts/02_Views.md) - Understanding Ivy views and components
## Examples
### Tracking Previous Values
```csharp
public class PreviousValueDemo : ViewBase
{
class PreviousValue { public int? Value = null; }
class Counter { public int Value = 0; }
public override object? Build()
{
var count = UseState(0);
var previousValue = this.UseRef(() => new PreviousValue());
var renderCount = this.UseRef(() => new Counter());
renderCount.Value.Value++;
// Get the previous value before updating it
var previous = previousValue.Value.Value;
var delta = previous.HasValue
? count.Value - previous.Value
: 0;
// Update previous value for next render (in real app, use UseEffect)
previousValue.Value.Value = count.Value;
return Layout.Vertical(
Text.P($"Current: {count.Value}"),
Text.P($"Previous: {previous?.ToString() ?? "None"}"),
Text.P($"Delta: {delta}"),
Text.P($"Renders: {renderCount.Value.Value}").Small(),
Layout.Horizontal(
new Button("+1", _ => count.Set(count.Value + 1)),
new Button("+5", _ => count.Set(count.Value + 5)),
new Button("Reset", _ => {
count.Set(0);
previousValue.Value.Value = null;
})
)
);
}
}
```
### Storing Mutable References
```csharp
public class MutableReferenceDemo : ViewBase
{
class RenderTracker { public int Count = 0; public DateTime LastRender = DateTime.Now; }
public override object? Build()
{
var count = UseState(0);
var tracker = this.UseRef(() => new RenderTracker());
// Mutate ref value without triggering re-render
tracker.Value.Count++;
tracker.Value.LastRender = DateTime.Now;
return Layout.Vertical(
Text.H3($"Count: {count.Value}"),
new {
RenderCount = tracker.Value.Count.ToString(),
LastRender = tracker.Value.LastRender.ToString("HH:mm:ss")
}.ToDetails(),
Text.P("Render tracker is stored in UseRef - it persists across re-renders but doesn't trigger them").Small(),
new Button("Increment", _ => count.Set(count.Value + 1))
);
}
}
```