IFeed<T> Interface
Base interface for all reactive feeds in DevBitsLab.Feeds.
Namespace: DevBitsLab.Feeds
Assembly: DevBitsLab.Feeds.dll
public interface IFeed<out T>
Overview
IFeed<T> defines the contract for reactive data sources with:
- State tracking — Loading, HasValue, HasError states
- Event notifications — StateChanged, PausedChanged
- Flow control — Pause/Resume updates
Implementations include Feed<T>, State<T>, and ListFeed<T>.
Properties
| Property | Type | Description |
|---|---|---|
State |
FeedState |
Current state as flags |
IsLoading |
bool |
Currently fetching data |
HasValue |
bool |
Data successfully loaded |
HasError |
bool |
Error occurred |
Value |
T? |
Current value (null if not loaded) |
Error |
Exception? |
Exception details (null if no error) |
IsPaused |
bool |
Updates are paused |
Events
StateChanged
Fires when the feed transitions between states.
event EventHandler<FeedStateChangedEventArgs>? StateChanged;
Example:
feed.StateChanged += (sender, e) => {
Console.WriteLine($"{e.OldState} → {e.NewState}");
// Detect load completion
if (e.OldState.HasFlag(FeedState.Loading) &&
e.NewState == FeedState.HasValue) {
OnLoadCompleted();
}
};
PausedChanged
Fires when pause state changes.
event EventHandler<bool>? PausedChanged;
Methods
RefreshAsync
Loads or reloads data from the source.
Task RefreshAsync();
Behavior:
- Cancels any in-progress load
- Sets
IsLoading = true - Preserves existing value during refresh (state =
Refreshing)
Example:
async Task OnPullToRefreshAsync()
{
await customerFeed.RefreshAsync();
}
Pause / Resume / TogglePause
Controls whether the feed accepts updates.
void Pause();
void Resume();
bool TogglePause(); // Returns new IsPaused state
Use Case: Prevent background updates during user editing.
void BeginEdit() {
customerFeed.Pause();
EnableForm();
}
void EndEdit() {
customerFeed.Resume();
}
State Transitions
┌─────────┐
start→ │ None │
└────┬────┘
│ RefreshAsync()
▼
┌─────────┐ ┌──────────┐
│ Loading │───────→│ HasValue │
└────┬────┘ success└────┬─────┘
│ │ RefreshAsync()
│ error ▼
└──────────┐
│Refreshing │ (Loading|HasValue)
└───────────┘
Checking States
// Simple checks
if (feed.IsLoading) ShowSpinner();
if (feed.HasValue) Display(feed.Value!);
if (feed.HasError) ShowError(feed.Error!);
// Combined state checks
if (feed.IsLoading && !feed.HasValue) {
// Initial load - full page spinner
}
if (feed.IsLoading && feed.HasValue) {
// Refreshing - subtle indicator
}
// Using State flags
if (feed.State == FeedState.Refreshing) {
ShowRefreshOverlay();
}
Best Practices
1. Always Check HasValue Before Accessing Value
// ✅ Good
if (feed.HasValue && feed.Value is { } customer) {
DisplayCustomer(customer);
}
// ❌ Risky - Value may be null
var name = feed.Value!.Name;
2. Handle All States in UI
feed.StateChanged += (s, e) => {
loadingPanel.Visible = feed.IsLoading;
contentPanel.Visible = feed.HasValue && !feed.IsLoading;
errorPanel.Visible = feed.HasError && !feed.IsLoading;
};
3. Dispose Event Subscriptions
public class MyViewModel : IDisposable {
private readonly Feed<Data> _feed;
public MyViewModel() {
_feed = Feed<Data>.Create(LoadAsync);
_feed.StateChanged += OnStateChanged;
}
public void Dispose() {
_feed.StateChanged -= OnStateChanged;
_feed.Dispose();
}
}
Thread Safety
All IFeed<T> implementations in DevBitsLab.Feeds are thread-safe:
Thread Safety Guarantees
| Operation | Thread-Safe |
|---|---|
Property access (Value, State, IsLoading, etc.) |
✅ Yes |
RefreshAsync |
✅ Yes |
Pause / Resume / TogglePause |
✅ Yes |
| Event subscription/unsubscription | ✅ Yes |
Safe Concurrent Access
// Access from multiple threads is safe
await Task.WhenAll(
Task.Run(() => Console.WriteLine(feed.Value)),
Task.Run(() => feed.RefreshAsync()),
Task.Run(() => feed.Pause())
);
Event Handler Threading
Important: Event handlers (
StateChanged,PausedChanged) execute on the thread that triggered the state change. When updating UI controls, marshal to the UI thread:
feed.StateChanged += (s, e) => {
// WPF
Application.Current.Dispatcher.Invoke(() => {
loadingSpinner.Visibility = feed.IsLoading
? Visibility.Visible
: Visibility.Collapsed;
});
// WinUI 3 / MAUI
DispatcherQueue.TryEnqueue(() => {
LoadingSpinner.IsActive = feed.IsLoading;
});
};
See Also
Feed<T>— Concrete implementationFeedState— State enum detailsState<T>— Editable feed with validationIListFeed<T>— List-specific interface