# UseState
Master reactive state management in Ivy using [hooks](../01_RulesOfHooks.md) like UseState, [UseSignal](./10_UseSignal.md), and [UseEffect](./04_UseEffect.md) to build dynamic, responsive [applications](../../../01_Onboarding/02_Concepts/10_Apps.md).
State management is a fundamental concept in Ivy that allows you to handle and update data within your [views](../../../01_Onboarding/02_Concepts/02_Views.md). Ivy provides several mechanisms for managing state, each suited for different use cases.
## Basic Usage
The `UseState` hook is the primary way to create reactive state in Ivy views:
```csharp
public class CounterApp : ViewBase
{
public override object? Build()
{
var count = UseState(0);
var name = UseState("World");
var client = UseService<IClientProvider>();
return new Card(
Layout.Vertical(
Text.Literal($"Hello, {name.Value}!"),
Text.Literal($"Count: {count.Value}"),
Layout.Horizontal(
new Button("Increment", _ => count.Set(count.Value + 1)),
new Button("Decrement", _ => count.Set(count.Value - 1)),
new Button("Reset", _ => count.Set(0))
),
new Separator(),
Layout.Horizontal(
name.ToTextInput().Placeholder("Your Name"),
new Button("Greet", _ => client.Toast($"Hello, {name.Value}!", "Greeting"))
)
)
).Title("Counter Demo");
}
}
```
### State with Factory Functions
For complex initialization or when you need to defer object creation, use factory functions with UseState. This pattern is useful for expensive computations, [dependency injection](./11_UseService.md), [memoization](./05_UseMemo.md), and [lazy loading](../../../01_Onboarding/02_Concepts/10_Apps.md):
```csharp
public class FactoryStateDemo : ViewBase
{
public override object? Build()
{
var expensiveData = UseState(() => ComputeExpensiveData());
var service = UseState(() => new DataService(GetConfig()));
return Layout.Vertical(
Text.P("Factory Functions Demo").Large(),
Text.Literal($"Data: {expensiveData.Value}"),
Text.Literal($"Service: {service.Value.Status}"),
new Button("Refresh", _ => expensiveData.Set(ComputeExpensiveData()))
);
}
private string ComputeExpensiveData() => $"Computed at {DateTime.Now:HH:mm:ss}";
private string GetConfig() => "production";
}
public class DataService
{
public string Status { get; } = "Connected";
public DataService(string config) { }
}
```
### State Types and Patterns
Ivy supports various state types including primitives, collections, complex objects, and nullable types. Each type has specific update patterns and considerations for optimal performance and maintainability:
```csharp
public class StatePatternsDemo : ViewBase
{
public override object? Build()
{
// Primitive types
var count = UseState(0);
var name = UseState("Guest");
// Collections
var items = UseState(() => new List<string> { "Item 1", "Item 2" });
// Complex objects
var user = UseState(() => new User { Name = "John", Age = 25 });
// Nullable types
var selectedItem = UseState(() => (string?)null);
return Layout.Vertical(
Text.P("State Types & Patterns").Large(),
// Primitive state
Layout.Horizontal(
new Button($"Count: {count.Value}", _ => count.Set(count.Value + 1)),
new Button(name.Value, _ => name.Set("User " + Random.Shared.Next(100)))
),
// Collection state
Layout.Horizontal(
new Button("Add Item", _ => {
var newList = new List<string>(items.Value) { $"Item {items.Value.Count + 1}" };
items.Set(newList);
}),
new Button("Clear", _ => {
var emptyList = new List<string>();
items.Set(emptyList);
})
),
// Object state
Layout.Horizontal(
new Button($"User: {user.Value.Name}", _ => {
var newUser = new User { Name = "Jane", Age = 30 };
user.Set(newUser);
})
),
// Nullable state
Layout.Horizontal(
new Button("Set Item", _ => selectedItem.Set("Selected Item")),
new Button("Clear Item", _ => {
string? nullValue = null;
selectedItem.Set(nullValue);
})
),
new Separator(),
Text.Literal($"Items: {string.Join(", ", items.Value)}"),
Text.Literal($"Selected: {selectedItem.Value ?? "None"}")
);
}
}
public class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
}
```
State updates in Ivy are handled through the Set method, which can accept direct values or computed values. Updates trigger automatic re-renders of the affected components, ensuring the UI stays synchronized with the current state:
```csharp
public class StateUpdatesDemo : ViewBase
{
public override object? Build()
{
var count = UseState(0);
var text = UseState("Hello");
var items = UseState(() => new List<string> { "Item 1", "Item 2" });
return Layout.Vertical(
Text.P("State Updates Demo").Large(),
// Direct updates
Layout.Horizontal(
new Button($"Count: {count.Value}", _ => count.Set(count.Value + 1)),
new Button("Reset", _ => count.Set(0))
),
// String updates
Layout.Horizontal(
text.ToTextInput("Enter text"),
new Button("Clear", _ => text.Set("")),
new Button("Uppercase", _ => text.Set(text.Value.ToUpper()))
),
// Collection updates
Layout.Horizontal(
new Button("Add Item", _ => {
var newItems = new List<string>(items.Value) { $"Item {items.Value.Count + 1}" };
items.Set(newItems);
}),
new Button("Clear", _ => items.Set(new List<string>()))
),
Text.Literal($"Text: {text.Value}"),
Text.Literal($"Items: {string.Join(", ", items.Value)}")
);
}
}
```
### State in Forms
[Forms](../../../01_Onboarding/02_Concepts/08_Forms.md) in Ivy use state variables bound to input widgets, allowing for real-time validation, live previews, and easy form submission handling. The ToTextInput, ToNumberInput, and ToBoolInput extensions provide seamless state binding:
```csharp
public class FormStateDemo : ViewBase
{
public override object? Build()
{
var name = UseState("");
var email = UseState("");
var age = UseState(18);
var isSubscribed = UseState(false);
var client = UseService<IClientProvider>();
return Layout.Vertical(
Text.P("Form State Demo").Large(),
name.ToTextInput("Name").Placeholder("Enter your name"),
email.ToTextInput("Email").Placeholder("Enter your email"),
age.ToNumberInput("Age").Min(0).Max(120),
isSubscribed.ToBoolInput("Subscribe to newsletter"),
new Separator(),
Layout.Horizontal(
new Button("Submit", _ => client.Toast($"Hello, {name.Value}!", "Greeting")),
new Button("Clear", _ => {
name.Set("");
email.Set("");
age.Set(18);
isSubscribed.Set(false);
})
),
Text.Literal($"Preview: {name.Value} ({email.Value}) - Age: {age.Value}")
);
}
}
```
The [UseEffect](./04_UseEffect.md) hook allows you to perform [side effects](./04_UseEffect.md) when state changes, such as updating derived state, making API calls, or triggering other actions. Effects run automatically when their [dependencies](./04_UseEffect.md) change:
```csharp
public class UseStateEffectsDemo : ViewBase
{
public override object? Build()
{
var count = UseState(0);
var lastUpdate = UseState(DateTime.Now);
var isEven = UseState(false);
// Effect that runs when count changes
UseEffect(() => {
lastUpdate.Set(DateTime.Now);
isEven.Set(count.Value % 2 == 0);
}, [count]);
return Layout.Vertical(
Text.P("State with Effects Demo").Large(),
Layout.Horizontal(
new Button($"Count: {count.Value}", _ => count.Set(count.Value + 1)),
new Button("Reset", _ => count.Set(0))
),
new Separator(),
Text.Literal($"Last Update: {lastUpdate.Value:HH:mm:ss}"),
Text.Literal($"Is Even: {(isEven.Value ? "Yes" : "No")}")
);
}
}
```