# UseTrigger
*The `UseTrigger` [hook](../01_RulesOfHooks.md) provides a way to conditionally render [components](../../../01_Onboarding/02_Concepts/02_Views.md) based on trigger state, commonly used for modals, dialogs, and other conditional UI elements. It manages visibility state and provides a callback to show the triggered component.*
## Overview
The `UseTrigger` [hook](../01_RulesOfHooks.md) enables conditional component rendering:
- **Conditional Rendering** - Show or hide components based on trigger state
- **Modal Support** - Perfect for modals, dialogs, and popups
- **State Management** - Automatic state management for trigger visibility
- **Callback Control** - Trigger callbacks to show or hide components
- **Value Passing** - Pass values to triggered components when showing them
> **info:** `UseTrigger` is ideal for conditional UI elements like modals, dialogs, dropdowns, and other components that need to be shown or hidden programmatically. The hook manages the visibility state internally and provides a callback to trigger the component display.
## Basic Usage
Use `UseTrigger` when you need to show/hide a component without passing parameters:
```csharp
public class SimpleTriggerExample : ViewBase
{
public override object? Build()
{
var (triggerView, showTrigger) = UseTrigger((IState<bool> isOpen) =>
isOpen.Value ? new ModalDialog(isOpen) : null);
return Layout.Vertical()
| new Button("Show Modal", onClick: _ => showTrigger())
| triggerView;
}
}
public class ModalDialog(IState<bool> isOpen) : ViewBase
{
public override object? Build()
{
return Layout.Vertical()
| Text.Block("This is a modal dialog")
| new Button("Close", onClick: _ => isOpen.Set(false));
}
}
```
### Trigger with Parameters
Use `UseTrigger<T>` when you need to pass data to the triggered component:
```csharp
public class TriggerWithParamsExample : ViewBase
{
public override object? Build()
{
var (triggerView, showTrigger) = UseTrigger((IState<bool> isOpen, int itemId) =>
new ItemDetailDialog(isOpen, itemId));
return Layout.Vertical()
| (Layout.Horizontal()
| new Button("Show Item 1", onClick: _ => showTrigger(1))
| new Button("Show Item 2", onClick: _ => showTrigger(2)))
| triggerView;
}
}
public class ItemDetailDialog(IState<bool> isOpen, int itemId) : ViewBase
{
public override object? Build()
{
if (!isOpen.Value) return null;
return Layout.Vertical()
| Text.Block($"Details for Item {itemId}")
| new Button("Close", onClick: _ => isOpen.Set(false));
}
}
```
## How Trigger Works
```mermaid
sequenceDiagram
participant C as Component
participant T as UseTrigger Hook
participant S as Trigger State
participant V as Trigger View
Note over C,V: Component calls UseTrigger
C->>T: UseTrigger(factory)
T->>S: Create trigger state
T->>V: Create trigger view
T-->>C: Return (view, callback)
Note over C,V: User triggers component
C->>T: Call showTrigger()
T->>S: Set state to true
S->>V: Render component via factory
V-->>C: Component displayed
Note over C,V: User closes component
V->>S: Set state to false
S->>V: Hide component
```
## Common Patterns
Use trigger with Alert for confirmation dialogs:
```csharp
public class DeleteConfirmationExample : ViewBase
{
public override object? Build()
{
var items = UseState(new List<string> { "Item 1", "Item 2", "Item 3" });
var (alertView, showAlert) = UseAlert();
return Layout.Vertical()
| Text.Block($"Items: {string.Join(", ", items.Value)}")
| (Layout.Horizontal()
| new Button("Delete #0", onClick: _ => showAlert("Delete item 0?", result =>
{
if (result == AlertResult.Ok)
{
var list = items.Value.ToList();
if (list.Count > 0) list.RemoveAt(0);
items.Set(list);
}
}, "Confirm"))
| new Button("Delete #1", onClick: _ => showAlert("Delete item 1?", result =>
{
if (result == AlertResult.Ok)
{
var list = items.Value.ToList();
if (list.Count > 1) list.RemoveAt(1);
items.Set(list);
}
}, "Confirm")))
| alertView;
}
}
```
## Troubleshooting
Ensure the trigger view is included in your component's return value:
```csharp
// Error: Trigger view not included
public override object? Build()
{
var (triggerView, showTrigger) = UseTrigger(...);
return Layout.Vertical() | new Button("Show", onClick: _ => showTrigger());
// Missing: triggerView
}
// Solution: Include trigger view
public override object? Build()
{
var (triggerView, showTrigger) = UseTrigger(...);
return Layout.Vertical()
| new Button("Show", onClick: _ => showTrigger())
| triggerView; // Include trigger view
}
```
## Best Practices
- **Check visibility state** - Always check `isOpen.Value` before rendering triggered content
- **Use provided state to close** - Call `isOpen.Set(false)` to close the component
- **Include trigger view in layout** - Always include `triggerView` in your return value
- **Use appropriate overload** - Simple for no params, generic `UseTrigger<T>` when passing data
## See Also
- [State](./03_UseState.md) - Component state management
- [Effect](./04_UseEffect.md) - Side effects and lifecycle
- [Rules of Hooks](../02_RulesOfHooks.md) - Understanding hook rules and best practices
- [Views](../../../01_Onboarding/02_Concepts/02_Views.md) - Understanding Ivy views and components
## Examples
### Modal Dialog with Alert
```csharp
public class ModalDialogExample : ViewBase
{
public override object? Build()
{
var (alertView, showAlert) = UseAlert();
var client = UseService<IClientProvider>();
return Layout.Vertical()
| new Button("Open Dialog", onClick: _ => showAlert("Are you sure you want to continue?", result =>
{
client.Toast($"You selected: {result}");
}, "Confirm Action"))
| alertView;
}
}
```
### Detail View with Sheet
```csharp
public class UserListSheetExample : ViewBase
{
public override object? Build()
{
return Layout.Horizontal()
| new Button("View Alice").WithSheet(
() => Layout.Vertical()
| Text.Block("Name: Alice")
| Text.Block("Email: alice@example.com"),
title: "User Details")
| new Button("View Bob").WithSheet(
() => Layout.Vertical()
| Text.Block("Name: Bob")
| Text.Block("Email: bob@example.com"),
title: "User Details");
}
}
```
### Edit Form Trigger
```csharp
public class DataTableEditExample : ViewBase
{
public override object? Build()
{
var items = UseState(new[] { "Item 1", "Item 2", "Item 3" });
var (editView, showEdit) = UseTrigger((IState<bool> isOpen, int index) =>
new EditForm(isOpen, items, index));
return Layout.Vertical()
| new List(items.Value.Select((item, index) =>
new ListItem(item, onClick: _ => showEdit(index))))
| editView;
}
}
public class EditForm(IState<bool> isOpen, IState<string[]> items, int index) : ViewBase
{
public override object? Build()
{
if (!isOpen.Value) return null;
var value = UseState(items.Value[index]);
return Layout.Vertical().Gap(2)
| value.ToTextInput()
| Layout.Horizontal().Gap(2)
| new Button("Save", onClick: _ =>
{
var updated = items.Value.ToArray();
updated[index] = value.Value;
items.Set(updated);
isOpen.Set(false);
})
| new Button("Cancel", onClick: _ => isOpen.Set(false));
}
}
```