# UseSignal

*Signals enable inter-component communication in Ivy [applications](../../../01_Onboarding/02_Concepts/10_Apps.md), allowing components to send and receive messages across the component tree.
They follow a publisher-subscriber pattern where components can send messages through signals and other components can listen for and respond to those messages.*

## Basic Usage

First, define a signal by creating a class that inherits from `AbstractSignal<TInput, TOutput>`:

```csharp
public class CounterSignal : AbstractSignal<int, string> { }

public class SignalExample : ViewBase
{
    public override object? Build()
    {
        var signal = CreateSignal<CounterSignal, int, string>();
        var output = UseState("");

        async ValueTask OnClick(Event<Button> _)
        {
            var results = await signal.Send(1);
            output.Set(string.Join(", ", results));
        }

        return Layout.Vertical(
            new Button("Send Signal", OnClick),
            new ChildReceiver(),
            output
        );
    }
}

public class ChildReceiver : ViewBase
{
    public override object? Build()
    {
        var signal = UseSignal<CounterSignal, int, string>();
        var counter = UseState(0);

        UseEffect(() => signal.Receive(input =>
        {
            counter.Set(counter.Value + input);
            return $"Child received: {input}, total: {counter.Value}";
        }));

        return new Card($"Counter: {counter.Value}");
    }
}
```

## Signal Communication Patterns

### One-to-Many Communication

This example demonstrates the one-to-many pattern where one sender broadcasts a message that multiple receivers all receive simultaneously.

```csharp
public class BroadcastSignal : AbstractSignal<string, Unit> { }

public class OneToManyDemo : ViewBase
{
    public override object? Build()
    {
        var signal = CreateSignal<BroadcastSignal, string, Unit>();
        var message = UseState("");
        var receiver1Message = UseState("");
        var receiver2Message = UseState("");
        var receiver3Message = UseState("");
        
        async ValueTask BroadcastMessage(Event<Button> _)
        {
            if (!string.IsNullOrWhiteSpace(message.Value))
            {
                await signal.Send(message.Value);
                message.Set("");
            }
        }
        
        // Set up signal receiver
        var receiver = UseSignal<BroadcastSignal, string, Unit>();
        
        // Process incoming messages
        UseEffect(() => receiver.Receive(message =>
        {
            // Each receiver processes the same message differently
            receiver1Message.Set($"Logged: {message}");
            receiver2Message.Set($"Analyzed: {message.Length} characters");
            receiver3Message.Set($"Stats: {message.Split(' ').Length} words");
            return new Unit();
        }));
        
        return Layout.Vertical(
            Layout.Horizontal(
                message.ToTextInput("Broadcast Message"),
                new Button("Send", BroadcastMessage)
            ),
            Layout.Horizontal(
                new Card(Text.Block(receiver1Message.Value)),
                new Card(Text.Block(receiver2Message.Value)),
                new Card(Text.Block(receiver3Message.Value))
            )
        );
    }
}
```

### Request-Response Pattern

This example demonstrates the request-response pattern where a requester sends a query and receives specific responses from providers. Unlike one-to-many broadcasting, this pattern expects specific data back from each provider.

```csharp
public class DataRequestSignal : AbstractSignal<string, string[]> { }

public class RequestResponseDemo : ViewBase
{
    public override object? Build()
    {
        var signal = CreateSignal<DataRequestSignal, string, string[]>();
        var query = UseState<string>("");
        var results = UseState<string[]>(() => Array.Empty<string>());
        var isSearching = UseState<bool>(false);
        
        async ValueTask SearchData(Event<Button> _)
        {
            if (!string.IsNullOrWhiteSpace(query.Value))
            {
                isSearching.Set(true);
                
                // Send request via signal and get responses from all providers
                var responses = await signal.Send(query.Value);
                var allResults = responses.SelectMany(r => r).ToArray();
                
                results.Set(allResults);
                query.Set("");
                isSearching.Set(false);
            }
        }
        
        return Layout.Vertical(
            Text.Block("Try searching for: John, Jane, Laptop, Smartphone, Tablet"),
            Layout.Horizontal(
                query.ToTextInput("Search"),
                new Button("Search", SearchData)
            ),
            Text.Block(isSearching.Value ? "Searching..." : $"Found {results.Value.Length} results"),
            results.Value.Select(r => Text.Block(r)),
            Layout.Horizontal(
                new DataProvider("User Database", new[] { "John Doe", "Jane Smith", "Bob Johnson" }),
                new DataProvider("Product Catalog", new[] { "Laptop", "Smartphone", "Tablet" })
            )
        );
    }
}

public class DataProvider : ViewBase
{
    private readonly string _providerName;
    private readonly string[] _dataSource;
    
    public DataProvider(string providerName, string[] dataSource)
    {
        _providerName = providerName;
        _dataSource = dataSource;
    }
    
    public override object? Build()
    {
        var signal = UseSignal<DataRequestSignal, string, string[]>();
        var processedQueries = UseState<int>(0);
        var lastQuery = UseState<string>("");
        
        UseEffect(() => signal.Receive(query =>
        {
            processedQueries.Set(processedQueries.Value + 1);
            lastQuery.Set(query);
            
            // Process the query and return results
            var matchingResults = _dataSource
                .Where(item => item.Contains(query, StringComparison.OrdinalIgnoreCase))
                .Select(item => $"[{_providerName}] {item}")
                .ToArray();
                
            return matchingResults;
        }));
        
        return new Card(
            Layout.Vertical(
                Text.Block(_providerName),
                Text.Block($"Data source: {string.Join(", ", _dataSource)}"),
                Text.Block($"Processed: {processedQueries.Value} queries"),
                Text.Block($"Last query: {lastQuery.Value}")
            )
        );
    }
}
```

### Key Types

- **`AbstractSignal<TInput, TOutput>`** - Base class for signals
- **`Unit`** - Void return type for notifications without responses
- **`CreateSignal<TSignal, TInput, TOutput>()`** - Creates signal sender
- **`UseSignal<TSignal, TInput, TOutput>()`** - Creates signal receiver

### Signal Operations

**Creating a sender** (to broadcast messages):

```csharp
var signal = CreateSignal<CounterSignal, int, string>();
await signal.Send(42); // Returns TOutput[] from all subscribers
```

**Creating a receiver** (to listen to messages):

```csharp
var signal = UseSignal<CounterSignal, int, string>();
UseEffect(() => signal.Receive(input => {
    // Handle message and return response
    return $"Processed: {input}";
}));
```

**Important**: Always use `UseEffect()` to manage signal subscriptions for proper [lifecycle handling](../../../01_Onboarding/02_Concepts/02_Views.md).

### Signal Types

- `TInput` - Data sent to subscribers
- `TOutput` - Response type from each subscriber (aggregated into array)

Use `Unit` when no response is needed, otherwise return meaningful data.

## Broadcast Types

### Contextual vs Broadcast Signals

By default, signals are **contextual** (scoped to component tree). For cross-session communication, add the `[Signal]` attribute:

```csharp
// Contextual - scoped to parent component tree
public class LocalSignal : AbstractSignal<string, Unit> { }

// Broadcast - scoped by BroadcastType
[Signal(BroadcastType.App)]
public class AppSignal : AbstractSignal<string, Unit> { }
```

### Available Broadcast Types

- **`App`** - All sessions running the same application
- **`Server`** - All active sessions on the server
- **`Machine`** - All sessions on the same physical machine
- **`Chrome`** - Parent [Chrome](../../../01_Onboarding/02_Concepts/16_Chrome.md) session (for embedded apps)

```csharp
[Signal(BroadcastType.App)]
public class AppNotificationSignal : AbstractSignal<string, Unit> { }
```

```mermaid
graph TB
    Sender[Signal Sender]
    App[App-Level]
    Server[Server-Level]
    Machine[Machine-Level]
    
    Sender --> App
    Sender --> Server
    Sender --> Machine
```

## See Also

- [State Management](../../03_Hooks/02_Core/03_UseState.md)
- [Effects](../../03_Hooks/02_Core/04_UseEffect.md)