FAQ
Do I need Tokio?
If you use DispatchRuntime / EffectRuntime, you’ll typically be running inside a Tokio runtime because:
- the runtimes use
tokio::select!internally - async patterns (
TaskManager,Subscriptions, async effects) obviously require it
If your app is fully synchronous and you don’t want Tokio, you can still use the lower-level pieces (Store, EffectStore, reducers, components) and write your own loop.
Where should async code live?
Not in the reducer.
Recommended:
- reducer mutates state and returns
DispatchResult<Effect> - effect handler executes effects (spawn tasks, do IO)
- async completion sends a normal action back (
Did*style)
This keeps reducers easy to test and makes side effects explicit.
How do I re-render when a component updates internal state?
Internal component state (cursor position, scroll offsets) lives in &mut self, so you need a way to request a render even when no app state changes.
Options:
- Return an app action that causes the reducer to return
true/DispatchResult::changed()(common, simple). - In your EventBus handler, return
HandlerResponse::ignored().with_render().with_consumed(true)when the component handled the event but emitted no actions.
If you’re writing reusable components, consider an explicit “Render” action (see examples/weather which has Action::Render).
For more on routing, see Event Bus.
What’s the difference between Effects and TaskManager?
Effectis your app’s enum describing side effects as data.TaskManageris a runtime helper that executes async work and gives you cancellation/debouncing.
Most apps use them together:
- reducer returns
Effect::FetchThing - effect handler uses
ctx.tasks().spawn("fetch", async { Action::DidLoad(...) })
How do I do periodic ticks / background polling?
Enable subscriptions and use Subscriptions (usually through EffectRuntime):
interval("tick", Duration::from_millis(100), || Action::Tick)interval_immediate(...)if you want an initial firestream(...)to forward a stream into actions
See Async Patterns and examples/weather.
How do I enable the debug overlay?
Use DebugLayer and wire it into the runtime:
DebugLayer::simple()usesF12as the toggle key- your state type must implement
DebugStatefor the state overlay
Example:
use tui_dispatch::debug::DebugLayer;
#[derive(Default, tui_dispatch::DebugState)]struct AppState { count: i32,}
let debug: DebugLayer<Action> = DebugLayer::simple().active(true);let mut runtime = DispatchRuntime::new(AppState::default(), reducer).with_debug(debug);While enabled:
Sopens the state treeAopens the action log
How do I test async flows?
You usually don’t test “async” directly.
Test the pieces:
- reducer emits the right
Effect - reducer handles
Did*actions correctly
EffectStoreTestHarness supports this well:
- dispatch intent -> drain effects
- complete
Did*action ->process_emitted()
See Tutorial: Fetching Data from an API.
See Also
- Quick Start - Quick start guide
- Async Patterns - Tasks, subscriptions, effects
- Debug Layer - State inspection and action logging
- Building Custom Components - Component trait guide