# UseEffect
*Perform side effects in your Ivy [views](../../../01_Onboarding/02_Concepts/02_Views.md) with the UseEffect [hook](../01_RulesOfHooks.md), similar to React's useEffect but optimized for server-side architecture.*
The `UseEffect` [hook](../01_RulesOfHooks.md) is a powerful feature in Ivy that allows you to perform side effects in your [views](../../../01_Onboarding/02_Concepts/02_Views.md). It's similar to React's useEffect hook but adapted for Ivy's architecture and patterns.
Effects are essential for handling operations that don't directly relate to rendering, such as working with [state](./03_UseState.md) updates, [async operations](../../../01_Onboarding/02_Concepts/06_TasksAndObservables.md), and external services:
```mermaid
graph TD
A[UseEffect] --> B[API calls & Data fetching]
A --> C[Timers & Intervals]
A --> D[Event subscriptions]
A --> E[Cleanup operations]
```
## Basic Usage
The simplest form of `UseEffect` runs after the component initializes:
```csharp
public class BasicEffectView : ViewBase
{
public override object? Build()
{
var message = UseState("Click the button to load data");
var loadTrigger = UseState(0);
// Effect runs when loadTrigger state changes
UseEffect(async () =>
{
if (loadTrigger.Value == 0) return; // Skip initial render
message.Set("Loading...");
await Task.Delay(2000); // Simulate API call
message.Set("Data loaded!");
}, loadTrigger);
return Layout.Vertical()
| new Button("Load Data", () => loadTrigger.Set(loadTrigger.Value + 1))
| Text.P(message.Value);
}
}
```
## Effect Overloads
Ivy provides four different overloads of `UseEffect` to handle various scenarios:
### Action Handler
For simple synchronous operations:
```csharp
UseEffect(() =>
{
Console.WriteLine("Component initialized");
});
```
### Async Task Handler
For asynchronous operations:
```csharp
UseEffect(async () =>
{
var data = await ApiService.GetData();
// Handle data...
});
```
### Disposable Handler
For operations that need cleanup:
```csharp
UseEffect(() =>
{
var timer = new Timer(callback, null, 0, 1000);
return timer; // Timer will be disposed when component unmounts
});
```
### Async Disposable Handler
For async operations with cleanup:
```csharp
UseEffect(async () =>
{
var connection = await ConnectToService();
return connection; // Connection will be disposed automatically
});
```
## Effect Triggers
Effects can be triggered by different events using trigger parameters:
```mermaid
graph LR
A[UseEffect] --> B[OnMount - Default]
A --> C[OnBuild]
A --> D[OnStateChange]
B --> B1["Runs once during initialization"]
C --> C1["Runs after virtual DOM updates"]
D --> D1["Runs when state changes"]
```
```csharp
// OnMount (default) - runs once during initialization
UseEffect(() => { /* ... */ });
UseEffect(() => { /* ... */ }, EffectTrigger.OnMount());
// OnBuild - runs after virtual DOM updates
UseEffect(() => { /* ... */ }, EffectTrigger.OnBuild());
// OnStateChange - runs when state changes
UseEffect(() => { /* ... */ }, EffectTrigger.OnStateChange(myState));
```
### State Dependencies
Effects can depend on [state](./03_UseState.md) changes:
```csharp
public class DependentEffectView : ViewBase
{
public override object? Build()
{
var count = UseState(0);
var message = UseState("Count: 0");
UseEffect(() =>
{
message.Set($"Count changed to: {count.Value}");
}, count);
return Layout.Vertical()
| new Button($"Count: {count.Value}",
() => count.Set(count.Value + 1))
| Text.P(message.Value);
}
}
```
### Multiple Dependencies
Effects can depend on multiple state variables:
```csharp
public class MultipleDepsView : ViewBase
{
public override object? Build()
{
var firstName = UseState("John");
var lastName = UseState("Doe");
var fullName = UseState("");
UseEffect(() =>
{
fullName.Set($"{firstName.Value} {lastName.Value}");
}, firstName, lastName);
return Layout.Vertical()
| (Layout.Horizontal()
| new Button($"First: {firstName.Value}",
() => firstName.Set(firstName.Value == "John" ? "Jane" : "John"))
| new Button($"Last: {lastName.Value}",
() => lastName.Set(lastName.Value == "Doe" ? "Smith" : "Doe")))
| Text.P($"Full name: {fullName.Value}");
}
}
```
## Common Patterns
### Data Fetching
Use `UseEffect` to fetch data from APIs or external services. The effect can be triggered by user interactions, state changes, or component initialization. Manage loading states to provide feedback during async operations.
```csharp
public class DataFetchView : ViewBase
{
public override object? Build()
{
var data = UseState<List<Item>?>();
var loading = UseState(false);
var loadTrigger = UseState(0);
UseEffect(async () =>
{
if (loadTrigger.Value == 0) return; // Skip initial render
loading.Set(true);
// Simulate API call - exceptions automatically handled by Ivy
await Task.Delay(1500);
var items = new List<Item>
{
new("Item 1", "Description 1"),
new("Item 2", "Description 2"),
new("Item 3", "Description 3")
};
data.Set(items);
loading.Set(false);
}, loadTrigger);
return Layout.Vertical()
| new Button("Fetch Data", () => loadTrigger.Set(loadTrigger.Value + 1))
| (loading.Value
? Text.P("Loading data...")
: Layout.Horizontal(
data.Value?.Select(item =>
new Button($"{item.Name}: {item.Description}")
) ?? Enumerable.Empty<Button>()
));
}
}
public record Item(string Name, string Description);
```
> **Info:** You do not need to manually catch exceptions in UseEffect. Ivy has a built-in exception handling pipeline that automatically catches exceptions from effects and displays them to users via error notifications and console logging. The system wraps effect exceptions in `EffectException` and routes them through registered exception handlers.
### Cleanup Operations
Return an `IDisposable` from `UseEffect` to perform cleanup when dependencies change or the component unmounts. This is essential for releasing resources like timers, subscriptions, or connections to prevent memory leaks. Store disposables in [UseRef](./08_UseRef.md) when you need to reference them across renders.
```csharp
public class SubscriptionView : ViewBase
{
public override object? Build()
{
var message = UseState("Stopped");
var isActive = UseState(false);
var previousResource = UseRef<IDisposable?>(() => null);
UseEffect(() =>
{
if (!isActive.Value)
{
var hadResource = previousResource.Value != null;
previousResource.Value?.Dispose();
previousResource.Value = null;
if (!hadResource) message.Set("Stopped");
return System.Reactive.Disposables.Disposable.Empty;
}
previousResource.Value?.Dispose();
message.Set("Running");
var resource = new SafeDisposable(() => message.Set("Cleaned up"));
previousResource.Value = resource;
return resource;
}, isActive);
return Layout.Vertical()
| new Button(isActive.Value ? "Stop" : "Start",
() => isActive.Set(!isActive.Value))
| Text.P($"Status: {message.Value}");
}
private class SafeDisposable : IDisposable
{
private readonly Action _onDispose;
private bool _isDisposed;
public SafeDisposable(Action onDispose) => _onDispose = onDispose;
public void Dispose()
{
if (_isDisposed) return;
_isDisposed = true;
_onDispose();
}
}
}
```
### Conditional Effects
Effects can be conditionally executed based on state values. Check conditions inside the effect and return early or conditionally create resources.
```csharp
public class ConditionalEffectView : ViewBase
{
public override object? Build()
{
var isEnabled = UseState(false);
var data = UseState<string?>();
UseEffect(async () =>
{
if (!isEnabled.Value)
{
data.Set((string)null);
return;
}
// Only fetch when enabled
var result = await FetchData();
data.Set(result);
}, isEnabled);
return Layout.Vertical()
| new Button($"Fetching: {(isEnabled.Value ? "ON" : "OFF")}",
onClick: _ => isEnabled.Set(!isEnabled.Value))
| (data.Value != null ? Text.P(data.Value) : Text.Muted("No data"));
}
private async Task<string> FetchData()
{
await Task.Delay(1000);
return $"Data fetched at {DateTime.Now:HH:mm:ss}";
}
}
```
## See Also
- [State Management](./03_UseState.md) - Managing component state
- [Rules of Hooks](../01_RulesOfHooks.md) - Understanding hook rules and best practices
- [Memoization](./05_UseMemo.md) - Optimizing performance with memoization
- [UseCallback](./06_UseCallback.md) - Memoizing callback functions
- [Signals](./10_UseSignal.md) - Reactive state management
- [Views](../../../01_Onboarding/02_Concepts/02_Views.md) - Understanding Ivy views and components