#状态管理 #dioxus #主题 #全局 #订阅 #系统 #响应性

dioxus-radio

为 Dioxus 提供具有主题订阅系统的完全类型化全局状态管理 🧬

6 个版本

0.2.4 2024年4月20日
0.2.3 2024年4月12日
0.2.1 2024年3月31日
0.1.1 2023年12月30日

#264 in GUI

Download history 158/week @ 2024-04-18 23/week @ 2024-04-25 10/week @ 2024-05-02 1/week @ 2024-05-09 4/week @ 2024-05-16 5/week @ 2024-05-23 16/week @ 2024-05-30 83/week @ 2024-06-06 67/week @ 2024-06-13 38/week @ 2024-06-20 21/week @ 2024-06-27 37/week @ 2024-07-04 67/week @ 2024-07-11 12/week @ 2024-07-18 33/week @ 2024-07-25 42/week @ 2024-08-01

157 次每月下载

MIT 许可证

32KB
275

Discord Server

dioxus-radio 📡🦀

Dioxus 🧬 提供具有主题订阅系统的完全类型化全局状态管理

适合人群

  • 你需要具有嵌套响应性的全局状态
  • 你真的不希望进行不必要的重新运行
  • 你希望有一个精确的状态订阅系统
  • 你不想不必要地克隆状态

支持

  • Dioxus v0.5 🧬
  • 所有渲染器(web桌面freya 等)
  • 支持 WASM 和原生目标

安装

安装最新版本

cargo add dioxus-radio

示例

cargo run --example demo

问题

你有一个单个的全局状态,但你的组件最终不必要地重新运行,因为尽管状态本身已更改,但并非所有组件都对新的突变状态感兴趣。相反,你希望有一个具有嵌套响应性的全局状态。

其他框架以自己的方式解决这个问题,例如,Solid 和其 Stores 允许你通过指定要突变的状态部分的路径来细粒度地突变状态,这使得 Solid 能够重新渲染读取该特定状态部分的组件。

这既不适用于 Rust 也不适用于 Dioxus,但幸运的是,有其他方法。

dioxus-radio 提供了不同的方法,为了在全局状态下实现细粒度订阅,你指明一个 Channel,这样,每次突变状态时,只有相同 Channel 的其他订阅者会收到通知。这种特定的模式由于使用枚举作为 Channel 的方式而非常适合 Rust。

示例

让我们想象我们想要一个应用,其中可能有 N 个元素,每个元素都有自己的状态。一开始你可能认为可以在每个组件实例中使用本地信号。但这个例子有一个限制,即状态必须是全局的,以便其他组件可以读取这些 N 个元素的状态。

以下是一个示例


// 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)]
pub enum DataChannel {
    ListCreated,
    ListN(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::ListCreated`
        // This way whenever a writer using the `DataChannel::ListCreated` mutates the state
        // This component will rerun
        let mut radio = use_radio::<Data, DataChannel>(DataChannel::ListCreated);

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

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

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

#[allow(non_snake_case)]
#[component]
fn ListComp(list_n: usize) -> Element {
    // Subscribe the state using the `DataChannel::ListN(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::ListN(list_n));

    println!("Running DataChannel::ListCreated({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}"
                    }
                }
            }
        }
    )
}

起源

dioxus-radio 的想法最初起源于我在 freya-editor 的工作中。我在优化状态管理时遇到了很多不必要的重新运行,所以我开始研究主题订阅状态管理。过了一段时间,我终于意识到我可以把这个功能导出到一个独立的库中。于是,我创建了 dioxus-radio,现在它实际上也为 freya-editor 提供了动力!

许可证

MIT 许可证

依赖关系

~4–10MB
~104K SLoC