Skip to content

Add Dioxus signals and hooks support to spacetime generate (needs rebase)#2

Open
Copilot wants to merge 28 commits intomasterfrom
copilot/explore-dioxus-signals-hooks
Open

Add Dioxus signals and hooks support to spacetime generate (needs rebase)#2
Copilot wants to merge 28 commits intomasterfrom
copilot/explore-dioxus-signals-hooks

Conversation

Copy link

Copilot AI commented Nov 30, 2025

Description of Changes

This PR adds support for generating Dioxus-compatible reactive code from SpacetimeDB module bindings. When using spacetime generate --lang dioxus, the following reactive hooks and signals are generated with full implementation for Dioxus 0.7+.

Generated Hooks and Signals

Connection Management:

  • use_spacetimedb_context_provider(uri, module_name) - Initialize connection at app root
  • use_spacetimedb_context() - Get context from parent component
  • use_connection() - Get the shared database connection (wrapped in Arc)
  • use_connection_state() - Reactive connection state signal
  • use_connection_error() - Reactive error signal

Subscriptions:

  • use_subscription(&["SELECT * FROM ..."]) - Subscribe to SQL queries

Table Hooks (generated per table):

  • use_table_<name>() - Returns Signal<Vec<TableRow>> with reactive updates via on_insert/on_delete callbacks

Reducer Hooks (generated per reducer):

  • use_reducer_<name>() - Returns callback that invokes the reducer

Procedure Hooks (generated per procedure):

  • use_procedure_<name>() - Returns callback that invokes the procedure

Implementation Details

In Dioxus 0.7+, Signals are Copy and thread-safe when their inner type is Send + Sync. Since Vec<T>, String, u64, bool etc. are all Send + Sync, we can directly write to Signals from SpacetimeDB callbacks by cloning the Signal handle.

The generated table hooks:

  1. Initialize with current table data from the cache
  2. Set up on_insert and on_delete callbacks that re-fetch all data from the cache
  3. This ensures data consistency and correct handling of all update scenarios

Current Status: Requires Rebase

⚠️ This branch has diverged significantly from master (278+ commits behind) and requires a rebase before merging. Master has major breaking API changes including:

  • New RawModuleDefV10 support in the schema
  • accessor_name field added to TypeDef and TableDef
  • New ReducerName type
  • CodegenOptions and CodegenVisibility added to the codegen API
  • Updated function signatures for iter_reducers, iter_procedures, iter_table_names_and_types

To resolve: Please rebase this branch onto master locally and force push, or create a new branch from master and re-apply the Dioxus changes.

Example Usage

use dioxus::prelude::*;
use crate::module_bindings::dioxus::*;

fn App() -> Element {
    let ctx = use_spacetimedb_context_provider("http://localhost:3000", "my-module");
    use_subscription(&["SELECT * FROM user"]);
    
    match ctx.state() {
        ConnectionState::Connected => rsx! { UserList {} },
        ConnectionState::Connecting => rsx! { "Connecting..." },
        _ => rsx! { "Disconnected" },
    }
}

fn UserList() -> Element {
    let users = use_table_user();
    let send_msg = use_reducer_send_message();
    
    rsx! {
        for user in users.read().iter() {
            div { "{user.name}" }
        }
        button {
            onclick: move |_| send_msg("Hello!".to_string()),
            "Send"
        }
    }
}

API and ABI breaking changes

None. This adds a new --lang dioxus option to the spacetime generate command without affecting existing functionality.

Expected complexity level and risk

Complexity: 2

The changes are self-contained within the codegen module and add a new language target without modifying existing generators. The Dioxus generator reuses the existing Rust type generation and adds a dioxus.rs module with reactive wrappers.

Testing

  • Added test_codegen_dioxus test case to validate Dioxus code generation
  • Generated and verified snapshot for Dioxus output
  • All 4 codegen tests pass (Rust, TypeScript, C#, Dioxus)
  • Unit tests for hook name generation (test_table_hook_name, test_reducer_hook_name)
  • CLI builds successfully with new --lang dioxus option
  • Verified generated code has correct Rust syntax (no parameter names in impl Fn() bounds)
  • Verified imports use correct namespace (::dioxus::prelude::*)
  • Verified table hooks implement full reactive updates with on_insert/on_delete callbacks
  • Verified reducer/procedure hooks invoke actual reducers/procedures via connection
  • Rebase onto master required before merge
Original prompt

This section details on the original issue you should resolve

<issue_title>Explore the possibility to create dioxus signals and hooks with spacetime generate</issue_title>
<issue_description>Analyse the project and try to find the most ergonomic way that spacetime could be used in conjunction with github.com/DioxusLabs/dioxus/</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@enaut
Copy link
Owner

enaut commented Nov 30, 2025

@copilot try to continue again

Copy link
Author

Copilot AI commented Nov 30, 2025

@copilot try to continue again

I've continued with the implementation. Added the missing Dioxus codegen test and generated the snapshot in commit 17bc9ff. All 4 codegen tests now pass (Rust, TypeScript, C#, and Dioxus).

The Dioxus generator creates reactive hooks for:

  • Connection management (use_spacetimedb_context_provider, use_connection_state, etc.)
  • Table subscriptions (use_subscription)
  • Per-table reactive signals (use_table_*)
  • Per-reducer callbacks (use_reducer_*)
  • Per-procedure callbacks (use_procedure_*)

Copilot AI changed the title [WIP] Explore creating dioxus signals and hooks with spacetime generate Add Dioxus signals and hooks support to spacetime generate Nov 30, 2025
@enaut
Copy link
Owner

enaut commented Nov 30, 2025

@copilot

evaluate and solve this user report:

Basierend auf den Kompilierungsfehlern sind hier die Probleme im Dioxus Code-Generator:

Probleme im spacetime generate --lang dioxus Generator

1. Ungültige Rust-Syntax in Fn Trait-Bounds (Zeilen 346, 406, 436, 466, 496)

// FALSCH generiert:
pub fn use_reducer_add_todo() -> impl Fn(text: String) + Clone + 'static

// RICHTIG sollte sein:
pub fn use_reducer_add_todo() -> impl Fn(String) + Clone + 'static

Problem: Parameter-Namen sind in impl Fn(...) Trait-Bounds nicht erlaubt.


2. Namenskonflikt mit dioxus Modul (Zeile 38)

// FALSCH generiert:
use dioxus::prelude::*;

// RICHTIG sollte sein:
use ::dioxus::prelude::*;

Problem: Da das Modul selbst dioxus.rs heißt, gibt es einen Namenskonflikt. Der Crate-Prefix :: wird benötigt.


3. Fehlende Trait-Imports (Zeilen 234, 304, 307, 314)

// FEHLT:
use spacetimedb_sdk::{DbContext, Table};

Problem: Die Methoden subscription_builder(), iter(), on_insert(), on_delete() benötigen diese Traits im Scope.


4. DbConnection ist nicht Clone (Zeilen 149, 206)

// FALSCH:
connection.set(Some(conn.clone()));
ctx.connection.read().clone()

Problem: DbConnection implementiert nicht Clone. Entweder muss Arc<DbConnection> verwendet werden, oder die Logik muss angepasst werden.


5. subscribe() API-Inkompatibilität (Zeile 245)

// FALSCH generiert:
.subscribe(queries.iter().map(|s| s.as_str()));

// RICHTIG sollte sein:
.subscribe(queries.clone());  // oder Vec<String> direkt

Problem: Der Iterator-Typ erfüllt nicht das IntoQueries Trait.


6. Dioxus Signals sind nicht Send/Sync (Zeilen 311, 318)

// FALSCH:
conn.db.todo().on_insert(move |_ctx, _row| {
    // Signal kann nicht in callback verwendet werden
    data.set(...);
});

Problem: Dioxus Signal<T> ist nicht thread-safe (!Send, !Sync). Die SpacetimeDB-Callbacks laufen möglicherweise auf anderen Threads.

Lösung: Entweder:

  • Channels verwenden (flume, tokio::sync::mpsc)
  • SyncSignal statt Signal verwenden
  • Oder Callbacks auf dem Dioxus-Runtime schedulen

Zusammenfassung der benötigten Fixes im Generator:

# Problem Datei/Zeile Fix
1 Parameter-Namen in Fn bounds dioxus.rs template Entferne name: aus impl Fn(name: Type)
2 Modul-Namenskonflikt dioxus.rs:38 Verwende ::dioxus::prelude::*
3 Fehlende Trait-Imports dioxus.rs:38-45 Füge DbContext, Table imports hinzu
4 DbConnection nicht Clone dioxus.rs Verwende Arc<DbConnection>
5 subscribe() API dioxus.rs:245 Korrigiere Query-Übergabe
6 Signal nicht Send/Sync dioxus.rs callbacks Verwende Channel oder SyncSignal

Copy link
Author

Copilot AI commented Nov 30, 2025

@copilot

evaluate and solve this user report:
...

Fixed all 6 reported issues in commit 1c3fcfc:

  1. Parameter names in Fn bounds - Now generates impl Fn(String) instead of impl Fn(name: String)
  2. Module namespace conflict - Using ::dioxus::prelude::*
  3. Missing trait imports - Added DbContext, Table imports
  4. DbConnection not Clone - Simplified context structure
  5. subscribe() API - Fixed to placeholder with documentation
  6. Signal not Send/Sync - Documented threading constraints with TODO notes

The generated code now compiles correctly. The hooks are currently placeholders that demonstrate the intended API, with documentation explaining the threading constraints that need to be addressed for full implementation.

Copilot AI changed the title Add Dioxus signals and hooks support to spacetime generate Add Dioxus signals and hooks support to spacetime generate (Proof of Concept) Nov 30, 2025
@enaut
Copy link
Owner

enaut commented Nov 30, 2025

@copilot Finish the PR implementing all remaining todos!

In Dioxus 0.7, Signals are Copy and thread-safe when their inner type is
Send + Sync. Since Vec, String, u64, bool etc. are all Send + Sync,
we can directly write to Signals from SpacetimeDB callbacks.
APPROACH: Clone Signal handle into callbacks

pub fn use_table_todo() -> Signal<Vec<Todo>> {
    let ctx = use_spacetimedb_context();
    let data: Signal<Vec<Todo>> = use_signal(Vec::new);
    
    use_effect(move || {
        // Get connection from context
        if let Some(conn) = ctx.connection.lock().unwrap().as_ref() {
            // Clone the signal handle (it's Copy, just an index)
            let data = data;
            
            // Register callbacks - Signal is Send + Sync
            conn.db().todo().on_insert(move |_ctx, todo| {
                data.write().push(todo.clone());
            });
            
            conn.db().todo().on_delete(move |_ctx, todo| {
                data.write().retain(|t| t.id != todo.id);
            });
            
            // Initial sync from existing data
            *data.write() = conn.db().todo().iter().collect();
        }
    });
    
    data
}

WHY THIS WORKS:

  • Signal is Copy (just a handle)
  • Signal is Send + Sync when T: Send + Sync
  • Vec, Todo, String, u64, bool are all Send + Sync
  • signal.write() can be called from any thread
    CONTEXT STRUCTURE:
pub struct SpacetimeDbContext {
    pub state: Signal<ConnectionState>,
    pub error: Signal<Option<String>>,
    pub connection: Arc<Mutex<Option<DbConnection>>>,
}

For reducers, just call them directly via the connection:

pub fn use_reducer_add_todo() -> impl Fn(String) + Clone {
    let ctx = use_spacetimedb_context();
    move |text: String| {
        if let Some(conn) = ctx.connection.lock().unwrap().as_ref() {
            conn.reducers().add_todo(text);
        }
    }
}

Copy link
Author

Copilot AI commented Nov 30, 2025

@copilot Finish the PR implementing all remaining todos!

In Dioxus 0.7, Signals are Copy and thread-safe when their inner type is
...

Implemented all remaining TODOs in commit cb45394.

The generated code now:

  • Stores DbConnection in Arc for thread-safety (SharedConnection type alias)
  • Provides use_connection() hook to access the shared connection
  • Implements use_subscription() with proper query passing
  • Table hooks initialize with current data and set up on_insert/on_delete callbacks that update signals
  • Reducer and procedure hooks call the actual reducers/procedures via the connection

As you noted, in Dioxus 0.7+ Signals are Copy and thread-safe when T: Send + Sync, so we can safely update them from SpacetimeDB callbacks.

Copilot AI changed the title Add Dioxus signals and hooks support to spacetime generate (Proof of Concept) Add Dioxus signals and hooks support to spacetime generate Nov 30, 2025
- `DbConnectionBuilder::build` becomes async without tokio's
block_in_place.

Still need to add `web` feature flag.
Replaces the unmaintained `anymap` dependency to fix a panic caused by
`copy_nonoverlapping::precondition_check` due to alignment in
`<TypeIdHasher as Hasher>::write` specific to web (wasm32) debug
builds.

The updated `anymap` fork drops its custom `Any` in favor of the
standard library's `std::any::Any`.
Renamed the `run_threaded` method on `wasm32` to better reflect its
behavior of spawning a background task.

The generated `DbConnection` methods `run_threaded`, `run_background`,
and `advance_one_message_blocking` now include runtime panics with a
clear error feedback when called on unsupported targets.
Copilot AI and others added 7 commits January 17, 2026 09:45
This adds a new code generation target for Dioxus framework integration.
The generator produces:
- use_spacetimedb_context_provider() for connection management
- use_subscription() for subscribing to SQL queries
- use_table_*() hooks for each table with reactive signals
- use_reducer_*() hooks for calling reducers
- use_procedure_*() hooks for calling procedures
- use_connection_state() and use_connection_error() for state tracking

The generated code uses Dioxus signals to automatically update UI
when database state changes.

Co-authored-by: enaut <290005+enaut@users.noreply.github.com>
- Added test_codegen_dioxus test case to codegen.rs
- Generated snapshot for Dioxus code output

Co-authored-by: enaut <290005+enaut@users.noreply.github.com>
Addresses user-reported compilation errors:

1. Fixed parameter names in `impl Fn()` trait bounds - now uses types only
   e.g. `impl Fn(String)` instead of `impl Fn(name: String)`

2. Fixed module namespace conflict - use `::dioxus::prelude::*` for absolute path

3. Added missing trait imports - `DbContext`, `Table` from spacetimedb_sdk

4. Addressed DbConnection not Clone - removed connection field from context

5. Fixed subscribe() API - simplified to placeholder implementation

6. Addressed Signal not Send/Sync - documented threading constraints,
   simplified hooks to placeholders with TODO notes for proper implementation

The generated code now provides:
- Correct Rust syntax that compiles
- Clear documentation of the intended API
- Notes about the threading constraints that need to be addressed
- Placeholder implementations that demonstrate the hook patterns

Co-authored-by: enaut <290005+enaut@users.noreply.github.com>
Implements all remaining TODOs for Dioxus 0.7+ integration:

1. Store DbConnection in context wrapped in Arc for thread-safety
2. Add use_connection() hook to access the shared connection
3. Implement use_subscription() with proper query passing
4. Implement table hooks with on_insert/on_delete callbacks that update signals
5. Implement reducer hooks to call reducers via connection
6. Implement procedure hooks to call procedures via connection

In Dioxus 0.7+, Signals are Copy and thread-safe when T: Send + Sync,
so Vec<T>, String, u64, bool etc. can be safely updated from callbacks.

The generated hooks now:
- Initialize signals with current table data
- Set up on_insert callbacks to push new rows to signal
- Set up on_delete callbacks to filter deleted rows from signal
- Provide working reducer/procedure invocation callbacks

Co-authored-by: enaut <290005+enaut@users.noreply.github.com>
Instead of manually tracking inserts/deletes, re-fetch all table data
from the cache on each update. This ensures:
- Data consistency (no race conditions between callbacks)
- Correct handling of update operations
- Simpler implementation with documented performance trade-offs

Added performance notes in documentation advising users about
the re-fetch behavior for tables with many rows.

Co-authored-by: enaut <290005+enaut@users.noreply.github.com>
@enaut enaut force-pushed the copilot/explore-dioxus-signals-hooks branch from 6e463d1 to 1399aae Compare January 17, 2026 08:59
@enaut enaut marked this pull request as ready for review January 17, 2026 12:56
@enaut enaut closed this Jan 17, 2026
enaut pushed a commit that referenced this pull request Feb 28, 2026
# Description of Changes

<!-- Please describe your change, mention any related tickets, and so on
here. -->

Copied from: clockworklabs#4084

We originally reverted this because it was causing testsuite flakes in
private. Now we have solved the issue that was causing the flakes so
this should be safe to merge.

Version upgrade to `v1.12.0`.

# API and ABI breaking changes

<!-- If this is an API or ABI breaking change, please apply the
corresponding GitHub label. -->

None

# Expected complexity level and risk

1 - this is just a version upgrade

<!--
How complicated do you think these changes are? Grade on a scale from 1
to 5,
where 1 is a trivial change, and 5 is a deep-reaching and complex
change.

This complexity rating applies not only to the complexity apparent in
the diff,
but also to its interactions with existing and future code.

If you answered more than a 2, explain what is complex about the PR,
and what other components it interacts with in potentially concerning
ways. -->

# Testing

<!-- Describe any testing you've done, and any testing you'd like your
reviewers to do,
so that you're confident that all the changes work as expected! -->

The testsuite failures are fixed by
clockworklabs#4120

- [x] License has been properly updated including version number and
date
- [x] CI passes

---------

Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com>
Co-authored-by: rekhoff <r.ekhoff@clockworklabs.io>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
@enaut enaut reopened this Feb 28, 2026
@enaut
Copy link
Owner

enaut commented Feb 28, 2026

@copilot resolve the conflicts and update to the new branch

@enaut
Copy link
Owner

enaut commented Feb 28, 2026

@copilot resolve the conflicts so that this pr can be merged into master again

Copy link
Author

Copilot AI commented Feb 28, 2026

@copilot resolve the conflicts so that this pr can be merged into master again

I attempted to resolve the conflicts but discovered that master has significant schema changes (RawModuleDefV10, accessor_name on TypeDef, ReducerName type, etc.) that require updating many files across multiple crates (codegen, schema, lib).

There are 278 commits on master since this branch diverged, including breaking API changes that can't be reconciled through simple file merges.

Options to resolve:

  1. Rebase (recommended): You would need to rebase this branch onto master from your local machine, then push. This would let git handle the conflicts properly.
  2. Recreate PR: Create a new branch from master and re-apply the Dioxus changes (the core changes are in dioxus.rs + minor additions to lib.rs, generate.rs, and codegen.rs test).

The Dioxus-specific changes are relatively self-contained:

  • crates/codegen/src/dioxus.rs - the main generator (~700 lines)
  • Import/export in crates/codegen/src/lib.rs
  • Language enum in crates/cli/src/subcommands/generate.rs
  • Test case in crates/codegen/tests/codegen.rs

I cannot perform rebases as that requires force push which is outside my capabilities.

Copilot AI changed the title Add Dioxus signals and hooks support to spacetime generate Add Dioxus signals and hooks support to spacetime generate (needs rebase) Feb 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Explore the possibility to create dioxus signals and hooks with spacetime generate

3 participants