1 个不稳定版本

0.1.0 2022 年 1 月 31 日

#13 in #Redux

Download history 19/week @ 2024-03-21 35/week @ 2024-03-28 35/week @ 2024-04-04 39/week @ 2024-04-11 16/week @ 2024-04-18 36/week @ 2024-04-25 50/week @ 2024-05-02 23/week @ 2024-05-09 19/week @ 2024-05-16 18/week @ 2024-05-23 24/week @ 2024-05-30 24/week @ 2024-06-06 20/week @ 2024-06-13 21/week @ 2024-06-20 13/week @ 2024-06-27

56 每月下载量

MIT 许可证

66KB
1.5K SLoC

Yewdux

Yew 应用程序提供简单的状态管理。

安装

将 Yewdux 添加到项目的 Cargo.toml

[dependencies]
yewdux = "0.7"

用法

Dispatch 是 Yewdux 的主要接口。它允许对全局应用程序状态进行共享可变访问,该状态存储在称为 Store 的专用代理容器中。

写入共享状态

创建一个 dispatch 很简单,只需给它一个存储类型。

use yewdux::prelude::*;

#[derive(Clone, Default)]
struct MyState {
    count: usize,
}

let dispatch = Dispatch::<BasicStore<MyState>>::new();

注意我们上面使用的是 BasicStore,这是 Yewdux 提供的默认存储类型之一。 也可以定义自己的存储类型

通过 reduce 修改状态。这里我们立即发送一个消息以将计数增加 1。

dispatch.reduce(|state| state.count += 1);

我们还可以创建执行相同操作的回调。此按钮每次点击都会发送消息。

let onclick = dispatch.reduce_callback(|state| state.count += 1);
html! {
    <button {onclick}>{"+1"}</button>
}

如果需要回调参数,可以使用 *_with 变体来访问。以下创建了一个新的回调,然后立即调用它,将计数增加 5。

let cb = dispatch.reduce_callback_with(|state, incr: usize| state.count += incr);
cb.emit(5);

读取共享状态

要读取状态,我们需要一个接收它的桥梁。状态在创建桥梁时接收一次,在状态更改后每次都接收。

use std::rc::Rc;

use yew::prelude::*;
use yewdux::prelude::*;

...

struct MyComponent {
    dispatch: Dispatch<BasicStore<MyState>>,
    state: Option<Rc<MyState>>,
}

enum Msg {
    State(Rc<MyState>),
}

impl Component for MyComponent {
    type Properties = (); 
    type Message = Msg;

    fn create(ctx: &Context<Self>) -> Self {
        // Create a bridge to receive new state. Changes are handled in `update`.
        let dispatch = Dispatch::bridge_state(ctx.link().callback(Msg::State));
        Self {
            dispatch,
            state: Default::default()
        }
    }

    fn update(&self, ctx: &Context<Self>, msg: Msg) -> bool {
        match msg {
            // Receive new state
            Msg::State(state) => {
                self.state = Some(state);
                true
            }
        }
    }

    ...
}

减少样板代码

为每个组件设置桥梁可能会很繁琐。提供了一种解决方案来自动处理此问题: WithDispatchDispatchProps

只需将 DispatchProps 属性提供给您的组件,并用 WithDispatch 组件包装器包装它。

重要:必须同时使用 WithDispatchDispatchProps,否则您的应用程序将崩溃。

struct MyComponent;
impl Component for MyComponent {
    type Properties = DispatchProps<BasicStore<MyState>>; 
    ...
}
html! {
    <WithDispatch<MyComponent> />
}

现在,您的组件将自动接收状态更新的更新。它的属性也像常规的 Dispatch 一样表现,唯一的区别是添加了一个获取状态的方法。

fn view(&self, ctx: &Context<Self>) -> Html {
    let onclick = ctx.props().reduce_callback(|s| s.count + 1);
    let count = ctx.props().state().count;

    html! {
        <>
        <p>{"Count is "}{ count }</p>
        <button {onclick}>{"+1"}</button>
        </>
    }
}

你注意到我们不必以这种方式处理 Option 吗?组件包装器会在首次接收状态时延迟渲染,使其使用起来更加人性化。

提示:为了节省一点输入,可以使用类型别名

type MyComponent = WithDispatch<MyComponentBase>;

struct MyComponentBase;
impl Component for MyComponentBase { .. }

html! {
    <MyComponent />
}

需要自定义属性吗?

WithDispatch 包装器可以与任何具有实现 WithDispatchProps 属性的组件一起使用。只需为您自己的属性实现它即可!这将来可能是一个宏。

#[derive(Properties, Clone, PartialEq)]
struct Props {
    dispatch: DispatchProps<BasicStore<MyState>>,
    ...
}

impl WithDispatchProps for Props {
    type Store = BasicStore<MyState>;

    fn dispatch(&self) -> &DispatchProps<Self::Store> {
        &self.dispatch
    }
}

持久性

Yewdux 支持状态持久性,因此您的应用重新加载时不会丢失状态。这要求您的状态还必须实现 SerializeDeserializePersistent

use serde::{Serialize, Deserialize};
use yewdux::prelude::*;

#[derive(Clone, Default, Serialize, Deserialize)]
struct MyState { ... };

impl Persistent for MyState {
    fn area() -> Area {
        Area::Session // Default is Area::Local
    }
}

struct MyComponent {
    dispatch: Dispatch<PersistentStore<State>>,
}

持久存储在启动时检查之前保存的状态,如果没有找到则使用默认值。状态在每次更改时都会保存。

函数式

Yewdux 支持函数式!

将其添加到您的项目中

[dependencies]
yewdux-functional = { git = "https://github.com/intendednull/yewdux.git" }

并享受简洁的好处

use yew::{prelude::*, functional::*};
use yewdux::prelude::*;
use yewdux_functional::*;


#[derive(function_component(MyComponent))]
fn my_component() -> Html {
    let store = use_store::<BasicStore<MyState>>();
    let onclick = store.dispatch().reduce_callback(|s| s.count += 1);
    let count = store.state().map(|s| s.count).unwrap_or_default();

    html! {
        <>
        <p>{"Count is "}{ count }</p>
        <button {onclick}>{"+1"}</button>
        </>
    }
}

示例

完整的示例可以在 这里 找到。

要运行示例,您需要安装 trunk,然后运行(将 [example] 替换为您想要的示例名称)

trunk serve examples/[example]/index.html --open

依赖项

~12MB
~226K SLoC