Claude Code Plugins

Community-maintained marketplace

Feedback

reactive-state-expert

@vanyastaff/flui
0
0

Expert on FLUI's reactive state management with signals, hooks, and effects. Use when discussing state, signals, use_signal, use_effect, reactivity, or data flow.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name reactive-state-expert
description Expert on FLUI's reactive state management with signals, hooks, and effects. Use when discussing state, signals, use_signal, use_effect, reactivity, or data flow.

Reactive State Expert

Expert skill for FLUI's reactive state management system.

When to Use

Activate this skill when the user:

  • Works with signals or reactive state
  • Uses hooks like use_signal, use_effect, use_memo
  • Discusses data flow or state updates
  • Debugs reactivity issues
  • Implements stateful widgets

Core Concepts

Signals

Copy-based reactive primitives with automatic dependency tracking.

// Create signal
let count = use_signal(ctx, 0);

// Read value
let current = count.get();

// Update value (triggers rebuild)
count.set(current + 1);

// Update with closure
count.update(|x| x + 1);

Signal Internals

// Signals use Copy semantics
pub struct Signal<T: Copy> {
    id: SignalId,
    // Value stored in global signal store (DashMap)
}

// Thread-safe with DashMap for lock-free access

Available Hooks

use_signal

// Basic state
let count = use_signal(ctx, 0);

// Computed from props
let doubled = use_signal(ctx, self.value * 2);

use_effect

// Side effects that run after build
use_effect(ctx, || {
    tracing::info!("Component mounted");
    
    // Cleanup function (optional)
    || {
        tracing::info!("Component unmounted");
    }
});

// With dependencies
use_effect_with_deps(ctx, (count.get(),), |deps| {
    tracing::info!("Count changed to: {}", deps.0);
    || {}
});

use_memo

// Expensive computation cached
let expensive = use_memo(ctx, || {
    compute_expensive_value()
});

// With dependencies
let result = use_memo_with_deps(ctx, (a, b), |(a, b)| {
    expensive_computation(*a, *b)
});

use_ref

// Mutable reference that doesn't trigger rebuilds
let dom_ref = use_ref(ctx, || None::<DomHandle>);

Critical Rules

Hook Order

// ALWAYS call hooks in the same order!

// BAD - conditional hook
if condition {
    let signal = use_signal(ctx, 0);  // PANIC!
}

// GOOD - always call, conditionally use
let signal = use_signal(ctx, 0);
if condition {
    signal.set(42);
}

No Loops

// BAD - variable hook count
for i in 0..items.len() {
    let signal = use_signal(ctx, i);  // PANIC!
}

// GOOD - use single signal with collection
let items = use_signal(ctx, vec![1, 2, 3]);

BuildContext is Read-Only

// Signal handles rebuild scheduling internally
let signal = use_signal(ctx, 0);
signal.set(42);  // Triggers rebuild via callback

// DON'T try to schedule rebuilds manually during build

State Management Patterns

Lifting State

// Parent owns state
struct Parent;
impl View for Parent {
    fn build(&self, ctx: &mut BuildContext) -> impl IntoElement {
        let count = use_signal(ctx, 0);
        
        Column::new(vec![
            Display { count: count.get() },
            Controls { on_increment: move || count.update(|x| x + 1) },
        ])
    }
}

Derived State

impl View for MyWidget {
    fn build(&self, ctx: &mut BuildContext) -> impl IntoElement {
        let count = use_signal(ctx, 0);
        
        // Derived computation in build - recalculated each time
        let is_even = count.get() % 2 == 0;
        
        // Or use memo for expensive derivations
        let expensive = use_memo(ctx, || {
            compute_from_count(count.get())
        });
    }
}

Async State

// Resource hook for async data
let users = use_resource(ctx, || async {
    fetch_users().await
});

match users.get() {
    ResourceState::Loading => LoadingSpinner {},
    ResourceState::Ready(data) => UserList { users: data },
    ResourceState::Error(e) => ErrorDisplay { error: e },
}

Debugging Reactivity

Tracing Updates

#[tracing::instrument]
fn build(&self, ctx: &mut BuildContext) -> impl IntoElement {
    let count = use_signal(ctx, 0);
    tracing::debug!(count = count.get(), "Building with count");
    // ...
}

Common Issues

Issue: Infinite rebuilds

// BAD - sets signal during build
let signal = use_signal(ctx, 0);
signal.set(signal.get() + 1);  // Infinite loop!

// GOOD - set in event handler
Button::new("Increment")
    .on_press(move || signal.update(|x| x + 1))

Issue: Stale closures

// BAD - closure captures old value
let handler = || {
    let current = count.get();  // Captured at build time
    process(current);
};

// GOOD - read inside closure
let handler = move || {
    process(count.get());  // Fresh value
};

Issue: Missing updates

// Signals use Copy - modifications don't propagate
let mut value = signal.get();
value += 1;  // Only modifies local copy!

// GOOD - use set/update
signal.set(signal.get() + 1);

Performance Tips

  1. Minimize signal scope: Keep signals as local as possible
  2. Use memo for expensive computations: Cache derived data
  3. Batch updates: Group related state changes
  4. Avoid deep nesting: Flatten state structure when possible