MCPcopy Index your code
hub / github.com/dioxus-community/dioxus-radio

github.com/dioxus-community/dioxus-radio @v0.7.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.7.1 ↗ · + Follow
60 symbols 104 edges 6 files 18 documented · 30%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Discord Server

dioxus-radio 📡🦀

Fully-typed global state management with a topics subscription system for Dioxus 🧬.

Who is this for

  • You want a global state with nested reactivity
  • You really don't want unnecessary re-runs
  • You want a precise state subscription system
  • You don't want to clone the state unnecessarily

Support

  • Dioxus v0.7 🧬
  • All renderers (web, desktop, freya, etc)
  • Both WASM and native targets

Questions

Feel free to open a discussions in the Discussions tab.

Installation

Install the latest release:

cargo add dioxus-radio

Example

cargo run --example demo

Problem

You have a single global state but your components end up rerunning unnecessarily because even though the state itself has changed, not all components are interested in the new mutated state. Instead, you want a global state with nested reactivity.

Other frameworks solve this in their own way, for instance, Solid and its Stores allow you to mutate the state granularly by requiring you to specify the path to the part of the state you want to mutate, this allows Solid to then rerender components that are reading from that specific part of the State.

This doesn't translate well to rust neither Dioxus, but luckily, there are other ways.

dioxus-radio presents a different approach, in order to have granular subscription with a global state you indicate a Channel, this way, whenever you mutate the state only other subscribers of the same Channel will be notified. This particular pattern translates quite well to Rust thanks to the usage of en Enums as Channels.

Example

Let's imagine we want an app where there might be N elements with each one having it's own state, at first you might think of simply using local signals in each component instance. But there is a constraint to this example, the state must be global so other components can read the state of those N elements.

Here is an example:


// Global state
#[derive(Default)]
struct Data {
    pub lists: Vec<Vec<String>>,
}

// Channels used to identify the subscribers of the State
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash)]
pub enum DataChannel {
    ListCreation,
    SpecificListItemUpdate(usize),
}

impl RadioChannel<Data> for DataChannel {}

fn main() {
    dioxus::launch(|| {
        // Initialize the global state
        use_init_radio_station::<Data, DataChannel>(Data::default);
        // Subscribe to the state with the channel `DataChannel::ListCreation`
        // This way whenever a writer using the `DataChannel::ListCreation` mutates the state
        // This component will rerun
        let mut radio = use_radio::<Data, DataChannel>(DataChannel::ListCreation);

        let onclick = move |_| {
            radio.write().lists.push(Vec::default());
        };

        println!("Running DataChannel::ListCreation");

        rsx!(
            button {
                onclick,
                "Add new list",
            }
            for (list_n, _) in radio.read().lists.iter().enumerate() {
                ListComp {
                    key: "{list_n}",
                    list_n
                }
            }
        )
    });
}

#[allow(non_snake_case)]
#[component]
fn ListComp(list_n: usize) -> Element {
    // Subscribe the state using the `DataChannel::SpecificListItemUpdate(list_n)` channel, where `list_n` is index of this element 
    // Whenever a mutation (in this case just this component) occurs only 
    // this component will rerun as it is the only one that is subscribed to this channel
    let mut radio = use_radio::<Data, DataChannel>(DataChannel::SpecificListItemUpdate(list_n));

    println!("Running DataChannel::SpecificListItemUpdate({list_n})");

    rsx!(
        div {
            button {
                onclick: move |_| radio.write().lists[list_n].push("Hello World".to_string()),
                "New Item"
            },
            ul {
                for (i, item) in radio.read().lists[list_n].iter().enumerate() {
                    li {
                        key: "{i}",
                        "{item}"
                    }
                }
            }
        }
    )
}

Origins

The idea of dioxus-radio originally started when I was working in freya-editor. I struggled to optimize the state management as I was doing many unnecessary reruns, so I started working in a topic-subscription state management. Some time passed and eventually, I realized I could export this to a separate library. So I made dioxus-radio and it now actually powers freya-editor as well!

License

MIT License

Extension points exported contracts — how you extend this code

RadioChannel (Interface)
(no doc) [3 implementers]
src/hooks/use_radio.rs
DataReducer (Interface)
(no doc) [1 implementers]
src/hooks/use_radio.rs
RadioReducer (Interface)
(no doc) [1 implementers]
src/hooks/use_radio.rs
DataAsyncReducer (Interface)
(no doc) [1 implementers]
src/hooks/use_radio.rs
RadioAsyncReducer (Interface)
(no doc) [1 implementers]
src/hooks/use_radio.rs

Core symbols most depended-on inside this repo

peek
called by 13
src/hooks/use_radio.rs
clone
called by 6
src/hooks/use_radio.rs
derive_channel
called by 3
src/hooks/use_radio.rs
read
called by 3
src/hooks/use_radio.rs
write
called by 3
src/hooks/use_radio.rs
notify_listeners
called by 2
src/hooks/use_radio.rs
cleanup
called by 2
src/hooks/use_radio.rs
subscribe_if_not
called by 2
src/hooks/use_radio.rs

Shape

Method 32
Function 10
Class 7
Enum 6
Interface 5

Languages

Rust100%

Modules by API surface

src/hooks/use_radio.rs42 symbols
examples/complex-demo.rs8 symbols
examples/async-demo.rs6 symbols
examples/simple-demo.rs4 symbols

For agents

$ claude mcp add dioxus-radio \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page