Dynamic Data Overview

This section covers three powerful tools for handling dynamic data in your Telex applications. Each serves a different purpose - choose based on your use case.

Quick Decision Guide

Use Streams when:

  • You have continuous background data that updates over time
  • Examples: timers, system monitoring, log tailing, live feeds
  • Data keeps flowing, no clear "done" state
  • Pattern: Background thread yielding values repeatedly

Use Effects when:

  • You need to react to state changes with side effects
  • Examples: logging, saving to disk, sending analytics, cleanup
  • One-time initialization or responding to dependency changes
  • Pattern: Run code after render when state changes

Use Async Data when:

  • You need to load data once with a clear start and end
  • Examples: API calls, database queries, file loading
  • Clear loading → ready/error lifecycle
  • Pattern: Background task with loading state management

Use Channels & Ports when:

  • External threads need to push data into your component
  • You need bidirectional communication with a background system
  • Examples: WebSocket handlers, hardware I/O, message queues
  • Pattern: Spawn thread with sender, drain messages each frame

Use Intervals when:

  • You need periodic callbacks on a fixed schedule
  • Examples: polling, animation ticks, blinking cursor
  • Pattern: Timer fires, callback runs on main thread each frame

Common Patterns

Timer/Clock Display

Use streams - continuous time updates:

let elapsed = stream!(cx, || {
    (0..).map(|s| {
        std::thread::sleep(Duration::from_secs(1));
        s
    })
});

Auto-save

Use effects - save when document changes:

effect!(cx, document.get(), |doc| {
    save_to_file("autosave.txt", doc);
    || {}
});

Loading User Profile

Use async - one-time fetch with loading state:

let profile = async_data!(cx, || {
    fetch_user_from_api()
});

match &profile {
    Async::Loading => View::text("Loading..."),
    Async::Ready(user) => View::text(&user.name),
    Async::Error(e) => View::text(e),
}

Combining Approaches

You can use multiple approaches together:

// Load initial data (async)
let data = async_data!(cx, || fetch_initial_data());

// Save changes (effect)
effect!(cx, data.clone(), |d| {
    if let Async::Ready(items) = d {
        save_to_disk(items);
    }
    || {}
});

// Show live stats (stream)
let stats = stream!(cx, || {
    (0..).map(|_| {
        std::thread::sleep(Duration::from_secs(5));
        get_stats()
    })
});

Next Steps

Start with the chapter that matches your use case: