Git Product home page Git Product logo

yewdux's Introduction

Yewdux

Ergonomic state management for Yew applications.

See the book for more details.

Example

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

#[derive(Default, Clone, PartialEq, Store)]
struct State {
    count: u32,
}

#[function_component]
fn ViewCount() -> Html {
    let (state, _) = use_store::<State>();
    html!(state.count)
}

#[function_component]
fn IncrementCount() -> Html {
    let (_, dispatch) = use_store::<State>();
    let onclick = dispatch.reduce_mut_callback(|counter| counter.count += 1);

    html! {
        <button {onclick}>{"+1"}</button>
    }
}

#[function_component]
fn App() -> Html {
    html! {
        <>
        <ViewCount />
        <IncrementCount />
        </>
    }
}

fn main() {
    yew::Renderer::<App>::new().render();
}

yewdux's People

Contributors

abusch avatar alilee avatar atmnk avatar christophbeberweil avatar dragon-hatcher avatar firstyear avatar intendednull avatar jtothethree avatar lukechu10 avatar nmlt avatar quetzal2 avatar wainwrightmark avatar zvolin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

yewdux's Issues

yewdux-functional won't compile

Compiling yewdux-functional v0.1.0 (https://github.com/intendednull/yewdux#8bbb5f9c)
error[E0308]: mismatched types
  --> /Users/pbsingh/DevTools/Rust/cargo/git/checkouts/yewdux-cc25a1d21ecd63a8/8bbb5f9/yewdux-functional/src/lib.rs:25:28
   |
25 |           self.output = Some(use_ref(move || {
   |  ____________________________^
26 | |             Dispatch::bridge(Default::default(), on_output.into())
27 | |         }));
   | |__________^ expected struct `RefCell`, found struct `Dispatch`
   |
   = note: expected struct `std::rc::Rc<RefCell<Dispatch<T, T>>>`
              found struct `std::rc::Rc<Dispatch<_, _>>`

error[E0308]: mismatched types
  --> /Users/pbsingh/DevTools/Rust/cargo/git/checkouts/yewdux-cc25a1d21ecd63a8/8bbb5f9/yewdux-functional/src/lib.rs:78:9
   |
78 |         dispatch,
   |         ^^^^^^^^ expected struct `RefCell`, found struct `Dispatch`
   |
   = note: expected struct `std::rc::Rc<RefCell<Dispatch<_>>>`
              found struct `std::rc::Rc<Dispatch<_>>`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `yewdux-functional` due to 2 previous errors

in yew version = "0.21.0" and yewdux = "0.9.3" error will generated

in use_store
let (store, dispatch):(Rc,Dispatch) = use_store::();
Error: the trait bound impl yew::functional::hooks::Hook<Output = (Rc<store::Store>, yewdux::dispatch::Dispatch<store::Store>)> + '_: Hook is not satisfied
the trait Hook is implemented for BoxedHook<'_, T>

derive Debug for Dispatch and DispatchProps?

Currently, Dispatch and DispatchProps does not implement Debug, which makes any component that contains these structs as fields to be unable to #[derive(Debug)]. Making it more difficult to debug these components.

Would it be possible to implement Debug for Dispatch and DispatchProps when STORE also implements Debug?

Middleware?

Interesting project!

I'm looking to retire my buggy home-grown copy of Redux in favor of something much more refined.

Where I'm a bit stuck is that it seems the yewdux design is lacking the notion of an "Action", and therefore I'm not sure that I can also retire my home-grown notion of Redux middleware.

Was the Redux action pattern eradicated in the yewdux design for less boilerplate, or for other reasons? (Obviously, I need to somehow recover it on top of yewdux if I am to support a proper middleware abstraction.)

Add PartialEq to StoreRef

It would be great if StoreRef supported PartialEq since this would allow for using the store as the dependency in use_effect_with_deps. I am using yewdux_function 0.1 and to get around this I used:

pub struct StoreWatcher {
    store_ref: StoreRef<PersistentStore<YewduxStore>>,
}

impl PartialEq for StoreWatcher {
    fn eq(&self, other: &Self) -> bool {
        self.store_ref.state() == other.store_ref.state()
    }
}

let store_watcher = StoreWatcher {
    store_ref: use_store::<PersistentStore<YewduxStore>>(),
};

use_effect_with_deps(||(), store_watcher);

This assumes that I can store a StoreRef this way to check on repeatedly. This seemed to work in my code where the first run was occuring with store.state() returning None, so at least in my case that seems to hold.

How do I use this?

I'm trying to create a store for popping up notifications to the user. But trying to follow the docs does not work for me.

From https://intendednull.github.io/yewdux/example.html:

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

#[derive(Debug, Default, Clone, PartialEq, Eq, Store)]
struct Counter {
    count: u32,
}
...

But when I implement this, I get the following error:

error: cannot find derive macro `Store` in this scope
 --> crates/frontend/src/components/notifier.rs:5:48
  |
5 | #[derive(Debug, Clone, Default, PartialEq, Eq, Store)]
  |                                                ^^^^^
  |
note: `Store` is imported here, but it is only a trait, without a derive macro

I see there is a yewdux-functional crate on crates.io but this isn't mentioned anywhere in the yewdux docs. Using the example from docs.rs seems to work.

use yew::prelude::*;
use yewdux::prelude::*;
use yewdux_functional::use_store;

#[derive(Clone, Default, PartialEq, Eq)]
pub struct Notification {
    pub msg: Option<String>,
    pub lvl: Option<String>,
}

#[function_component(Notifier)]
pub fn notifier() -> Html {
    let store = use_store::<BasicStore<Notification>>();
    let msg = store.state().map(|s| s.msg).unwrap_or(None);
    let lvl = store
        .state()
        .map(|s| s.lvl.unwrap_or_else(|| "".to_string()))
        .unwrap();
    let dismiss = store.dispatch().reduce_callback(|_| Notification {
        ..Default::default()
    });

    match msg {
        Some(message) => html! {
            <div class="container">
                <div
                    class={match lvl.as_str() {
                        "warn" => "alert alert-warning",
                        "error" =>  "alert alert-danger",
                        _ => "alert alert-info",
                    }}
                    onclick={dismiss}
                    role="alert">
                    {message}
                </div>
            </div>
        },
        None => html! {},
    }
}

But I'm not sure how to update this state from another component. In the example below, if the form submission received a 400 response from the request to the backend, I'd like to display a notification in the <Notifier /> component which is rendered at the top of the tree.

#[function_component(NewKey)]
pub fn new_key() -> Html {
    let name = use_state(|| "".to_string());
    let description = use_state(|| "".to_string());
    let store = use_store::<BasicStore<Notification>>();

    let onsubmit = {
        let history = use_history().unwrap();
        let name = name.clone();
        let description = description.clone();

        Callback::once(move |e: FocusEvent| {
            e.prevent_default();
            wasm_bindgen_futures::spawn_local(async move {
                let resp = Request::post("api/keys")
                    ...;

                match resp.status() {
                    400 => {
                        store.state().unwrap().msg = Some("Failed".to_string());
                        store.state().unwrap().lvl = Some("error".to_string());
                    }
                    _ => history.push(Route::Keys),
                }
            });
        })
    };
    
    html!{
    ...
    }
}

But the compiler tells me cannot assign to data in an 'Rc'. Any guidance would be appreciated and I can open a PR to update the docs once I understand what I should do here.

Thank you.

Feature Suggestion: history/undo/redo

Hi. I pattern I use quite often is a history store that enables undo/redo functionality for a particular store.

It's quite a common pattern and can be implemented generically so I wonder if it would be worth creating a macro to automatically generate it.

usage would look something like this

#[derive(Default, Clone, PartialEq, Eq, Debug)]
#[store(history)]
struct CounterStore {
    count: i32,
}

#[function_component]
fn Controls() -> Html {
    let (state, dispatch) = use_store::<CounterStoreHistoryState>();

    let on_undo_click = dispatch.apply_callback(|_| CounterStoreHistoryMessage::Undo);
    let on_redo_click = dispatch.apply_callback(|_| CounterStoreHistoryMessage::Redo);

    html!(
        <div>
            <button onclick={on_undo_click} disabled={undo_disabled}>{"Undo"}</button>
            <button onclick={on_redo_click} disabled={redo_disabled}>{"Redo"}</button>
        </div>
    )
}

The CounterStoreHistoryState and CounterStoreHistoryMessage structs are generated by the macro.

Happy to do a PR if you think this is a good idea - I've already written the code a couple of times, just need to write a macro to generate it.

It seems yewdux not working on safari

my code like this:

let (store, dispatch) = use_store::<Store>();
let user = store.auth_user.clone();
console_log!("{:#?}", user);
#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq, Store)]
#[store(storage = "local", storage_tab_sync)]
pub struct Store {
    pub auth_user: Option<User>,
    pub page_loading: bool,
    pub alert_input: AlertInput,
}

pub fn set_auth_user(user: Option<User>, dispatch: Dispatch<Store>) {
    dispatch.reduce_mut(move |store| {
        store.auth_user = user;
    })
}

When i use Chrome, console log show:
ๆˆชๅฑ2023-03-09 ไธ‹ๅˆ3 59 35

When i use safari, console log show always None :
ๆˆชๅฑ2023-03-09 ไธ‹ๅˆ4 02 31

Maybe storage_tab_sync ? or other problem ?

Panic with BorrowMutError in yewdux::mrc::Mrc

I managed to narrow this down to a minimal example:

use yew::prelude::*;
use yewdux::prelude::*;
fn main() {
    wasm_logger::init(wasm_logger::Config::default());
    yew::start_app::<Model>();
}
#[function_component(Model)]
fn test() -> html {
    let (points, dispatch) = use_store::<Points>();
    let cb = dispatch.reduce_callback(|points| Points {
        points: points.points + 1,
    });
    let html = (0..points.points)
        .map(|_| {
            html! { <div>
                <Listener/>
            </div>}
        })
        .collect::<Vec<_>>();
    html! {
        <div>
            <button onclick={cb}>{"Click me"}</button>
            {html}
        </div>
    }
}
#[function_component(Listener)]
fn listener() -> html {
    let (points, _) = use_store::<Points>();
    html! { {points.points}}
}
#[derive(Store, PartialEq, Eq, Default, Clone)]
struct Points {
    points: usize,
}

This panics with a BorrowMutError when clicking the button. Stacktrace:




ย  | imports.wbg.__wbg_error_09919627ac0992f5 | @ | yewdux_test-b5ad39e837318030.js:298
-- | -- | -- | --
ย  | $console_error_panic_hook::error::h1bf004dd5dd797d3 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x89e18
ย  | $console_error_panic_hook::hook_impl::h2f812b50a8f0b4ee | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x46af3
ย  | $console_error_panic_hook::hook::h87dfebef834b97cb | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xdd092
ย  | $core::ops::function::Fn::call::hb5125e048d9483c1 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xcfcbf
ย  | $std::panicking::rust_panic_with_hook::h8888bbc201384d7b | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x70d70
ย  | $std::panicking::begin_panic_handler::{{closure}}::hea311ea2bd92a293 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x97390
ย  | $std::sys_common::backtrace::__rust_end_short_backtrace::h7df6c1a4b8949645 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xe4948
ย  | $rust_begin_unwind | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xd981d
ย  | $core::panicking::panic_fmt::h18b15be283411c65 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xda52a
ย  | $core::result::unwrap_failed::he84b58f6b44a7df4 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xa16bc
ย  | $core::result::Result<T,E>::expect::h3be6d4948a308f88 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x8a44a
ย  | $core::cell::RefCell<T>::borrow_mut::h6ea2ee2c4c616321 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x957c7
ย  | $yewdux::mrc::Mrc<T>::borrow_mut::h142978717a104ae5 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x8fd45
ย  | $yewdux::subscriber::<impl yewdux::mrc::Mrc<yewdux::subscriber::Subscribers<S>>>::subscribe::hd5e737e518c8a197 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x4db1c
ย  | $yewdux::dispatch::subscribe_silent::h5d904aff3a057541 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x52084
ย  | $yewdux::dispatch::Dispatch<S>::subscribe_silent::h5fc3187de10b37d9 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x60b52
ย  | $yewdux::functional::use_store::{{closure}}::h6b1cc4bf1f406180 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xacb20
ย  | $yew::functional::hooks::use_state::use_state::{{closure}}::h9e7fd398f9da59d5 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x92529
ย  | $yew::functional::hooks::use_reducer::use_reducer_base::{{closure}}::hb062d1a9b3d25667 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x78081
ย  | $yew::functional::hooks::use_hook::{{closure}}::h48bf44ea4b54c8a0 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x280fc
ย  | $scoped_tls_hkt::ScopedKeyMut<T>::with::{{closure}}::hffe18d03ef49d574 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x82a5a
ย  | $scoped_tls_hkt::ScopedKeyMut<T>::replace::he6ba7b33de1346a1 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x6209c
ย  | $scoped_tls_hkt::ScopedKeyMut<T>::with::h348c7ce78b7032ba | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x8e683
ย  | $yew::functional::hooks::use_hook::h298c206310724745 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x2f590
ย  | $yew::functional::hooks::use_reducer::use_reducer_base::hd9240f079f2ddf4e | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xad395
ย  | $yew::functional::hooks::use_reducer::use_reducer::hb388a6c61784b344 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xad32e
ย  | $yew::functional::hooks::use_state::use_state::h7ca065e0df553d47 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x64ebe
ย  | $yewdux::functional::use_store::hfbbf76e937f2ac8e | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x4ba9a
ย  | $<yewdux_test::listener as yew::functional::FunctionProvider>::run::h586cf77fdf950556 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x68ba4
ย  | $<yew::functional::FunctionComponent<T> as yew::html::component::Component>::view::{{closure}}::hf25fd87fb7ff370a | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xce049
ย  | $scoped_tls_hkt::ScopedKeyMut<T>::set::{{closure}}::h3edcc5e30a510a39 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xd51d0
ย  | $scoped_tls_hkt::ScopedKeyMut<T>::replace::h7207ef6fcd16b62f | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x7d3c4
ย  | $scoped_tls_hkt::ScopedKeyMut<T>::set::hb0d3513ad957a88c | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xb257d
ย  | $yew::functional::FunctionComponent<T>::with_hook_state::he9f5ec900265b1c7 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x7d6e5
ย  | $<yew::functional::FunctionComponent<T> as yew::html::component::Component>::view::hc9f8f79bb6a459d8 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xb6ef1
ย  | $<yew::html::component::lifecycle::RenderRunner<COMP> as yew::scheduler::Runnable>::run::h62d62b05cad1f717 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x16939
ย  | $yew::scheduler::start::{{closure}}::hff3797e886ac8179 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x285a5
ย  | $std::thread::local::LocalKey<T>::try_with::h9e78c40b52aa8483 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x70038
ย  | $std::thread::local::LocalKey<T>::with::h52c5a0213964e7ea | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xb18f7
ย  | $yew::scheduler::start::h1da8427f37f92d11 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xe4a24
ย  | $yew::html::component::scope::Scope<COMP>::push_update::h5347c5c0d2d61c28 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x7033e
ย  | $yew::html::component::scope::Scope<COMP>::send_message::hb359b9baaa4b7636 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x95fcd
ย  | $<yew::functional::FunctionComponent<T> as yew::html::component::Component>::create::{{closure}}::ha626eacd102907e7 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xa55d6
ย  | $yew::functional::HookUpdater::callback::h963690d127ca2972 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x3ca90
ย  | $yew::functional::hooks::use_reducer::use_reducer_base::{{closure}}::{{closure}}::h0d479cf880ffa1dc | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xa652d
ย  | $yew::functional::hooks::use_reducer::UseReducerHandle<T>::dispatch::hd800521261ec9dac | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x84b35
ย  | $yew::functional::hooks::use_state::UseStateHandle<T>::set::h4872376548d49de2 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xd524a
ย  | $yewdux::functional::use_store::{{closure}}::{{closure}}::h1d1991e0a6c1b94d | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xd5119
ย  | $<F as yewdux::subscriber::Callable<S>>::call::hcbeeaa73d29bc53c | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xc7e0d
ย  | $yewdux::subscriber::<impl yewdux::mrc::Mrc<yewdux::subscriber::Subscribers<S>>>::notify::h8b1c40e7ec48f1ff | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x4696f
ย  | $yewdux::dispatch::notify_subscribers::h75bed5ed2a6abf5d | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x6cc6d
ย  | $yewdux::dispatch::reduce::h26152b03e40f2580 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x57f10
ย  | $yewdux::dispatch::Dispatch<S>::reduce_callback::{{closure}}::hf079313223315938 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xc199c
ย  | $yew::callback::Callback<IN>::emit::h70402f57b2567734 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x44ec9
ย  | $<yew::html::listener::events::onclick::Wrapper as yew::virtual_dom::listeners::Listener>::handle::h5ec2809813994f2b | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xcede2
ย  | $yew::virtual_dom::listeners::Registry::run_handlers::{{closure}}::h7181ab016b56a6fc | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x1bf82
ย  | $yew::virtual_dom::listeners::Registry::run_handlers::hfa834aba393fdf4d | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x395ba
ย  | $yew::virtual_dom::listeners::Registry::handle::h0acf85ab015152be | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x62718
ย  | $yew::virtual_dom::listeners::GlobalHandlers::ensure_handled::{{closure}}::{{closure}}::h0378e231be9d3fee | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0xbb83a
ย  | $<dyn core::ops::function::Fn<(A,)>+Output = R as wasm_bindgen::closure::WasmClosure>::describe::invoke::h5974d5da5247ef08 | @ | yewdux_test-b5ad39e8โ€ฆ030_bg.wasm:0x7e9f0
ย  | __wbg_adapter_18 | @ | yewdux_test-b5ad39e837318030.js:214
ย  | real

As far as I understand it, if you increase the amount of state listeners while using the state, it panics. Is there any way to fix this?

Documentation: Clarify how the store can be accessed outside of a yew component

Hi there,

I am learning rust since about a year and yew/yewdux since a few weeks in my free time. One thing that took a lot of time and quite some help from the discord community was to figure out how to access the state in rust code that is not a yew component, so use_state() does not yield the state and dispatch, but a Hook instead.

Acually, the solution is in the documentation, but I have read it a lot of times but simply did not understand it correctly, and I focussed on use_state or alternatives instead.

I'd like to add a small change to the documentation in order to emphasize that

Dispatch::<Counter>::new();

can be used to access the dispatcher and the state from anywhere :)

Component Not Updating when State Changes

I have state for a Sidebar defined in sidebar.rs as such:

#[derive(Debug, Clone, PartialEq, Eq, Store)]
pub struct SidebarState
{
	pub open		: bool,
	pub active_menu	: String,
}

My component is as follows in menu.rs:

#[derive(Properties, PartialEq)]
pub struct MenuHamburgerProps
{
	#[prop_or_default]
	children : Children,
}

#[function_component]
pub fn MenuHamburger(props : &MenuHamburgerProps)
-> Html
{
	let (state, dispatch) = use_store::<SidebarState>();
    let on_menu_click = dispatch.reduce_mut_callback(|state| 
		{
			state.open = !(state.open);
			log!("Sidebar open=", state.open);
		});
	html!
	{
		<div id="menu">
            <div id="menu-icon" onclick={on_menu_click}>
                <i className="fa fa-bars"></i>
            </div>
			if state.open
			{
				{props.children.clone()}
			}
			else
			{
				<div className="hidden">
					{props.children.clone()}
				</div>
			}
        </div>
	}
}

It is then used later in main.rs via:

fn switch(routes: Route)
-> Html
{
    match routes
	{
        Route::Home => html! {<></>},
        Route::NotFound => html! { <h1>{ "404" }</h1> },
    }
}

#[function_component(Main)]
fn app_view()
-> Html
{
    return html!
	{
		<>
			<TopNavbar>
				<Sidebar>
				</Sidebar>
			</TopNavbar>
			<BrowserRouter>
				<Switch<Route> render={switch} />
			</BrowserRouter>
		</>
    };
}

fn main()
{
    yew::Renderer::<Main>::new().render();
}

The problem is that onclick it is not rendering the children of MenuHamburger outside of the hidden div even though the callback registers the state change (via console.log)

How to delete a store

Hi - I cannot seem to find in the documentation or the book a reference to some method to clear out the stored entries in the session or local storage.

How can one do that?

Have a way to inject listeners into derived `Store` implementation

Hi. I love this crate - I use it in all my yew projects.

At the moment it's a bit of a pain to use listeners because I am almost always using the Derive attribute to implement Store and persist to local storage. If I want to add a listener to a Store with zero listeners I need to expand the macro and add the init_listener statements. This is a bit ugly and potentially error prone. It would be nice to have an attribute to initialize extra listeners in the generated code.

For example you could write something like:

use yewdux::{store::Store, prelude::Listener};
pub struct ExampleListener;

impl Listener for ExampleListener{
    type Store = ExampleStore;

    fn on_change(&mut self, state: std::rc::Rc<Self::Store>) {
        log::info!("Changed")
    }
}

#[derive(Default, PartialEq, Store)]
#[store(storage = "local", storage_tab_sync )]
#[store_listener(ExampleListener)]
pub struct ExampleStore{
    prop: bool
}

Instead of:

use yewdux::{store::Store, prelude::Listener};
use yewdux::prelude::init_listener;
pub struct ExampleListener;

impl Listener for ExampleListener{
    type Store = ExampleStore;

    fn on_change(&mut self, state: std::rc::Rc<Self::Store>) {
        log::info!("Changed")
    }
}

#[derive(Default, PartialEq)]
pub struct ExampleStore{
    prop: bool
}

impl Store for ExampleStore {
    #[cfg(not(target_arch = "wasm32"))]
    fn new() -> Self {
        init_listener(ExampleListener);
        Default::default()
    }

    #[cfg(target_arch = "wasm32")]

    fn new() -> Self {
        init_listener(StorageListener);
        init_listener(ExampleListener);

        storage::load(storage::Area::Local)
            .ok()
            .flatten()
            .unwrap_or_default()
    }

    fn should_notify(&self, other: &Self) -> bool {
        self != other
    }
}

struct StorageListener;
impl Listener for StorageListener {
    type Store = ExampleState;

    fn on_change(&mut self, state: Rc<Self::Store>) {
        #[cfg(target_arch = "wasm32")]
        save(state.as_ref(), Area::Local).expect("unable to save state");
    }
}

Happy to have a go and do a PR if you think this is a good idea.

Cannot move out of ...

I can't seem to wrap my head around why the following is happening:

#[derive(Clone, Debug, Default, PartialEq, Store)]
pub struct Foo {
    foo: String,
}
impl Foo {
    pub fn update(&mut self, new: String) {
        self.foo = new;
    }
}

#[function_component]
pub fn Tabs() -> Html {
    let (foo, foo_dp) = use_store::<Foo>();

    let id = uuid::Uuid::new_v4().to_string();
    let update = {
        let id = id.clone();
        foo_dp.reduce_mut_callback_with(|foo, e: MouseEvent| {
            foo.update(id);
        })
    };

    //    html!  and friends ...

Resulting in an error on the update call stating:

cannot move out of `id`, a captured variable in an `Fn` closure
move occurs because `id` has type `std::string::String`, which does not implement the `Copy` trait

With the latest yewdux and yew as dependencies.

Any ideas?

Intermediating for Server

Thanks for this crate. I love the economy of your implementation - very impressive re-use of a small number of core concepts. It is hard to get my head around it, but can definitely appreciate the art.

I would like to make a Store like Relation<X> (a hashmap of uuids to X's) which has a Reducer which speculatively applies the change to the local Store but also makes an async Request of the Reducer message to a remote server API (as a mutation) which would respond with the updated/persisted X (or maybe an error based on validation).

Any thoughts about an example which puts the Store between yew and a server? Do you think it would be possible? My attempt has run into weird ownership issues with the Store update inside a spawn_local inside Reducer::apply.

Confusing regarding multi-component reducer example

Trying to implement global state into an application, but struggling to understand how to access DispatchProps from a sub-component. It doesn't seem like DispatchProps::bridge_state exists...

Any documentation or help on this would be greatly appreciated. As soon as I understand it I would love to help contribute to these docs. I've found several examples which doesn't compile, which ideally should be brought up to date.

The Id of the handler, while present in the slab, is not associated with a callback

Using
yew = "0.19"
yewdux = "0.7"

When having any store, for example (does not have to be persistent):

  use serde::{Serialize, Deserialize};
  use yewdux::prelude::*;
  
  pub type ThemeStore = PersistentStore<Theme>; 
  
  #[derive(Clone, PartialEq, Default, Serialize, Deserialize)]
  pub struct Theme {
      pub is_dark_mode: bool,
  }
  
  impl Persistent for Theme {
      fn area() -> Area {
          Area::Session // Default is Area::Local
      }
  }

And using the store in a component like:

fn create(ctx: &Context<Self>) -> Self {
    Self {
        ...
        theme_dispatch: Dispatch::bridge_state(ctx.link().callback(Msg::ThemeUpdated)),
        theme_state: Default::default(),
    }
}

fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
    match msg {
        Msg::ThemeUpdated(new_state) => {
            self.theme_state = new_state;
            true
        }
        Msg::ToggleDarkMode => {
            self.theme_dispatch
                .reduce(|theme| theme.is_dark_mode = !theme.is_dark_mode);
            false
        }
    }
}

on the call of self.theme_dispatch.reduce, the following warning is printed to the console:

The Id of the handler: 2, while present in the slab, is not associated with a callback.

This, as stated above, happens with ANY call to reduce. Creating the dispatcher with Dispatch::new() (if I don't want't to be notified about state-changes) does not make a difference.
Did I miss anything or should I do something differently?

No initial state-event for persisted state using a struct component

When using the new Store macro as in https://github.com/intendednull/yewdux/tree/master/examples/persistent, that is, using a function component, everything works as expected.

But when using the persistent store in a struct component with:

Self {
    dispatch: Dispatch::<State>::subscribe(ctx.link().callback(Msg::StateUpdated)),
    state: Default::default(),
    ...

When the component is created with an old state persisted in the browser, that state is not passed to the component with a Msg::StateUpdated message. The state field inside the component struct will just stay to what Default::default() returned.
Only a state change happening after the component was created will pass the current state to the component using the message.

Questions: Cannot use a selector on a String

I have the following state :

#[derive(Clone, PartialEq, Eq, Deserialize, Serialize, Store, Default)]
#[store(storage = "local", storage_tab_sync)]
pub struct RootState {
    pub(crate) id_token: String,
    pub(crate) other: String
}

I'm trying to use the use_selector function in a yew component on my id_token.

let id_token = use_selector(|state: &RootState| state.id_token.clone());
info!("id_token {}", *id_token);

I have the following error :

error[E0614]: type `impl yew::functional::hooks::Hook<Output = Rc<std::string::String>> + '_` cannot be dereferenced

I've tried multiple things but I can't find the way to display my id_token in the log or even in the Html.
Any recommendation to fix this issue?

Does the store get dropped when a component dismounts?

I was looking at the implementation of use store and I saw it used a threadlocal to do the global state.

If a component is dismounted, and itโ€™s the only component using a certain store, does the store get dropped?

Large states and storage

In some occasions, the storage listener has been slowing down my Yew application due to the sheer size of the serialization effort involved before writing to local/session storage.

Would you have any ideas how to alleviate this or keep it from blocking the main application's thread?

I was hoping to use some form of Async+spawn_local or WebWorker setup to run the serialization effort in parallel, but so far I haven't been able to get it going and do any good.

  • async/spawn_local would be awesome if it wouldn't end up just blocking the main thread anyway as soon as it's web_sys storage call starts.
  • WebWorker like with yew-agent, while this would seem great, the entire store would still have to be sent over the wire to the worker as bincode. While it would be awesome to lift this burden off of the main thread, the next hurdle would be to ensure that data-races are prevented, such that the "storaged" state eventually ends up as the latest one.

As a last resort I could turn away from using storage listeners altogether and only use a manual save button somewhere such that regular interaction with the app remains smooth.

I have a problem with the ReducerStore and BasicStore

I dont know if I can ask some help here, but I have been stuck for a while now.
https://github.com/tykim83/text_adventure_v4_yewdux

I have this project which has two components and a shared state with actions.
If I use ReducerStore for both components after 2 Action one of the 2 component does't get updated and the prgram crashes.
But If I use BasicStore for the main component the main component doesn't get updated but the the logic on state and the other component works.

Could anyone have a look and give me some advice?
Thank you

no function or associated item named `global` found for struct `yewdux::Dispatch`

I write code with given example Default Value, an error occured

no function or associated item named `global` found for struct `yewdux::Dispatch`

here is my code

use yewdux::prelude::*;

#[derive(PartialEq, Store)]
struct MyStore {
    foo: String,
    bar: String,
}

impl Default for MyStore {
    fn default() -> Self {
        Self {
            foo: "foo".to_string(),
            bar: "bar".to_string(),
        }
    }
}
fn main() {
    let foo = "foo".to_string();
    let bar = "bar".to_string();
    // Run this before starting your app.
    Dispatch::<MyStore>::global().set(MyStore { foo, bar });
}

here is the Cargo.toml

[dependencies]
yewdux = "0.10.0"

I want to know if I should turn on a certain feature?

Latest Yewdux won't compile

unresolved imports `yew_agent::Agent`, `yew_agent::AgentLink`, `yew_agent::Context`
 --> $CARGO_HOME/git/checkouts/yewdux-cc25a1d21ecd63a8/d076aa4/yewdux/src/service.rs:8:17
  |
8 | use yew_agent::{Agent, AgentLink, Bridge, Bridged, Context, Dispatched, Dispatcher, HandlerId};
  |                 ^^^^^  ^^^^^^^^^                   ^^^^^^^ no `Context` in the root
  |                 |      |
  |                 |      no `AgentLink` in the root
  |                 no `Agent` in the root

error[E0432]: unresolved import `yew_agent::AgentLink`
 --> /Users/pbsingh/DevTools/Rust/cargo/git/checkouts/yewdux-cc25a1d21ecd63a8/d076aa4/yewdux/src/store/link.rs:8:17
  |
8 | use yew_agent::{AgentLink, HandlerId};
  |                 ^^^^^^^^^ no `AgentLink` in the root

For more information about this error, try `rustc --explain E0432`.

Is there any memory limitation?

Hi, I'm using yewdux for a simple app to persist the local state, I'm uploading images into the state but when I refresh only 4 of them remain persistent, 1.5MB in total. Is there any method to increase it or to solve the problem?

Unable to set and retrieve state.

I have an Store setup called AuthStore,

use serde::{Deserialize, Serialize};
use yewdux::store::Store;

#[derive(Default, Clone, PartialEq, Store, Serialize, Deserialize)]
#[store(storage = "local")] // or session
pub struct AuthStore {
    pub username: String,
    pub password: String,
    pub is_authenticated: bool,
}

and then I use this to set values from user creation form,

use crate::{
    components::forms::create_user_form::{CreateFormStateData, CreateUserForm},
    router::Route,
    stores::auth_store::AuthStore,
};
use course_byte_shared::{create_user, NewUser, User};
use gloo::console;
use yew::{function_component, html, use_state, Callback, Html};
use yew_router::prelude::{use_navigator, Link};
use yewdux::prelude::use_store;

#[function_component(CreateUser)]
pub fn create_user() -> Html {
    let new_user_data = use_state(User::default);

    let navigator = use_navigator().unwrap();

    let (store, dispatch) = use_store::<AuthStore>();
    let dp = dispatch.clone();
    let store_c = store.clone();

    let on_submit_form = Callback::from(move |data: CreateFormStateData| {
        let new_user_data_clone = new_user_data.clone();

        let navigator_clone = navigator.clone();

        let dispatch = dispatch.clone();
        let store = store.clone();

        wasm_bindgen_futures::spawn_local(async move {
            let navigator_clone = navigator_clone.clone();

            let dispatch = dispatch.clone();
            let store = store.clone();

            match create_user(NewUser {
                username: data.username.to_string(),
                email: data.email.to_string(),
                password: data.password.to_string(),
            })
            .await
            {
                Ok(user) => {
                    let username = user.username.clone();
                    let token = user.token.clone();
                    let token_c = user.token.clone();

                    new_user_data_clone.set(user);

                    dispatch.reduce_mut(move |state| {
                        state.username = username;
                        state.password = token;
                        state.is_authenticated = true;
                    });

                    console::log!("Token: ", token_c);
                }
                Err(error) => eprintln!("API Error: {:?}", error),
            };

            // console::log!("Sucessfully created new user, redirecting to home page..");
            // navigator_clone.push(&Route::Home);
        });
    });

    html! {
        <div>
            <h1> { "Create user" } </h1>
            <CreateUserForm on_submit={ on_submit_form }/>
            // <h2> { "Username: " } { dp.get().username.clone() } </h2>
            // <h2> { "Password: " } { dp.get().password.clone() } </h2>
            <h2> { "Username: " } { store_c.username.clone() } </h2>
            <h2> { "Password: " } { store_c.password.clone() } </h2>
            <h2> { "Is authenticated: " } { store_c.is_authenticated } </h2>
            <span style="margin: 4px;"></span>
            <Link<Route> to={ Route::Home }>{ "Back to home" }</Link<Route>>
        </div>
    }
}

But I don't see anything in browser, the h2 for Username and Password stay empty.

I tried adding another field count to see if it adds up,

use serde::{Deserialize, Serialize};
use yewdux::store::Store;

#[derive(Default, Clone, PartialEq, Store, Serialize, Deserialize)]
#[store(storage = "local")] // or session
pub struct AuthStore {
    pub username: String,
    pub password: String,
    pub is_authenticated: bool,
    pub count: u32,
}
use crate::{
    components::forms::create_user_form::{CreateFormStateData, CreateUserForm},
    router::Route,
    stores::auth_store::AuthStore,
};
use course_byte_shared::{create_user, NewUser, User};
use gloo::console;
use yew::{function_component, html, use_state, Callback, Html};
use yew_router::prelude::{use_navigator, Link};
use yewdux::prelude::use_store;

#[function_component(CreateUser)]
pub fn create_user() -> Html {
    let new_user_data = use_state(User::default);

    let navigator = use_navigator().unwrap();

    let (store, dispatch) = use_store::<AuthStore>();
    let dp = dispatch.clone();
    let store_c = store.clone();

    let on_submit_form = Callback::from(move |data: CreateFormStateData| {
        let new_user_data_clone = new_user_data.clone();

        let navigator_clone = navigator.clone();

        let dispatch = dispatch.clone();
        let store = store.clone();

        wasm_bindgen_futures::spawn_local(async move {
            let navigator_clone = navigator_clone.clone();

            let dispatch = dispatch.clone();
            let store = store.clone();

            match create_user(NewUser {
                username: data.username.to_string(),
                email: data.email.to_string(),
                password: data.password.to_string(),
            })
            .await
            {
                Ok(user) => {
                    let username = user.username.clone();
                    let token = user.token.clone();
                    let token_c = user.token.clone();

                    new_user_data_clone.set(user);

                    dispatch.reduce_mut(move |state| {
                        state.username = username;
                        state.password = token;
                        state.is_authenticated = true;
                        state.count += 5;
                    });

                    console::log!("Token: ", token_c);
                }
                Err(error) => eprintln!("API Error: {:?}", error),
            };

            // console::log!("Sucessfully created new user, redirecting to home page..");
            // navigator_clone.push(&Route::Home);

            console::log!("Count after update: ", store.count);
        });
    });

    html! {
        <div>
            <h1> { "Create user" } </h1>
            <CreateUserForm on_submit={ on_submit_form }/>
            // <h2> { "Username: " } { dp.get().username.clone() } </h2>
            // <h2> { "Password: " } { dp.get().password.clone() } </h2>
            <h2> { "Username: " } { store_c.username.clone() } </h2>
            <h2> { "Password: " } { store_c.password.clone() } </h2>
            <h2> { "Count: " } { store_c.count } </h2>
            <h2> { "Is authenticated: " } { store_c.is_authenticated } </h2>
            <span style="margin: 4px;"></span>
            <Link<Route> to={ Route::Home }>{ "Back to home" }</Link<Route>>
        </div>
    }
}

But the count stays at 0.

Am I doing something wrong here?

Computed values in Yewdux Store

Hi guys I am very new to Yewdux and Yew and I am struggling how to create a computed value in a store that derives from two other values in the store. This value has relatively many calculations and I would not like this code to be a part of a component file. I tried intercepting the state in a Listener but I can not seem to update the store from the listener on_change() function, which I realize is not a good idea sinse I could set an infinite loop in motion. Any ideas how can I achieve that without invoking the dispatch in the component code?

Confusion on make_mut tip on documentation

The documentation on https://intendednull.github.io/yewdux/dispatch.html#mutate-state-predictably has a part that says:

Tip
Rc::make_mut is handy if you prefer CoW:

impl Reducer<Counter> for Msg {
    fn apply(&self, mut counter: Rc<Counter>) -> Rc<Counter> {
        let state = Rc::make_mut(&mut counter);

        match self {
            Msg::AddOne => state.count += 1,
        };

        counter
    }
}

Shouldn't the return value be Rc::new(state)? Since make_mut will clone the value if there are other outstanding Rcs pointing to the object, mutating state inside the match block will mutate a fresh clone of it, which effectively gets discarded at the end of the function.

Edit:
Actually, Rc::new(state) doesn't work either, because it wants Counter, while state is only &mut Counter.

Edit2:
I managed to make it work by:

impl Reducer<Counter> for Msg {
    fn apply(&self, mut counter: Rc<Counter>) -> Rc<Counter> {
        let state = (*state).clone();

        match self {
            Msg::AddOne => state.count += 1,
        };

        Rc::new(state)
    }
}

Does this look ok, or am I using it incorrectly?

Component example not working

I believe this is a bug in the not-stable release. At a glance, the trait requirements for Dispatch::subscribe(...) don't seem to permit the defacto yew Callback<IN> from being used.

#[derive(Default, PartialEq, Store)]
pub struct FrameStore {
    frame: i32,
}

impl Component for App {
  //...
  fn create(ctx: &Context<Self>) -> Self {
      let frame_update_callback = ctx.link().callback(Signal::UpdateFrame);
      let dispatch = Dispatch::<FrameStore>::subscribe(frame_update_callback);
      // ...
  }
  //...
}

image

On another note, just came across this project today - exactly what I was looking for! Thanks!

anymap versions < 1.0-beta-X are unsound; the crate seems unmaintained

  • The unsoundness results in a random crash at program start, in copy_nonoverlapping
  • More details here and specifically here
  • The author has pushed two "prereleases", 1.0-beta1 and 1.0-beta2. The latter one seems to have the problem fixed. Haven't tested with the former, but likely it fixes the problem as well.

Unfortunately, I cannot hit the issue with your examples, but I hit it every single time with my project (see the 2nd comment for a stacktrace excerpt, which crashes in copy_nonoverlapping). I've also checked that updating anymap to 1.0-beta2 fixes the problem for me - in both yewdux itself as well as in my own yewdux-middleware.

What I would suggest:

  • Update the dependency of anymap to V 1.0-beta2. I don't think it is worth it to switch to anymap2, as it does not seem much more active
  • The dependency should be updated in both master and somehow in 0.8.X. Given that you branch for each patch version, not sure how to do the latter though? Shall I just branch from 0.8.2 and publish as a new branch - 0.8.3 with the anymap dependency updated?

use_selector is composable, but dispatch isn't

use_selector is a great way to create reusable components that operate only on a slice of the global store and are otherwise unaware of what the global store type actually is (by parameterizing the component with a generic parameter that represents the store and passing via props the selector function).

The idea is you create a reusable component, along with its store slice and reducers and then it can be plugged in a bigger app. As this one.

No such luck with the dispatcher in yewdux though. Dispatcher always takes the global Store object. In pure Redux with pure untyped JavaScript, the global nature of the dispatcher is not a problem because:
(a) Redux actions are untyped;
(b) More importantly, the reducers for each slice of the store are written in such a way, that an unknown action always "falls-through" and returns the particular slice unchanged;
(c) Finally, the root reducer is composed in a way where each slice reducer sees each action. (reference)

Given the typed nature of Rust though, we need to "select" or - rather - "project" the root dispatcher into a local one by using a user-supplied projection function. Say, use_dispatcher(|state: &mut S| &mut state.my_slice) which only operates on the particular slice that the component is interested in updating.

reduce() hangs forever

Hi,
Soo I am trying to create a very small test application with yewdux-functional.
This is my root component with a simple state struct.

#[function_component(Test)]
pub fn test() -> Html {
    let state = use_store::<BasicStore<TestState>>();
    state.dispatch().reduce(|_| {});
    html! {
        {"Test123"}
    }
}

#[derive(Clone, Debug, PartialEq, Default)]
pub struct TestState {}

But as soon as I include the .reduce(|_|{}) call, my browser tab starts to load forever and (I assume) busy waits one of my cores completely.
Am I not allowed to mutate the state directly in a function without callbacks etc?
This seems like a very trivial usecase to me.

Suggestion: Provide standard implementations for persisted stores

If we want a store with persistence, going by the current state of the Readme, we always need to implement even the (sane) default implementation that simply deserializes on load and serializes on every change ourself. Something like this:
(I changed it slightly, so that it logs an error instead of panicking. Think this is fine here. Don't know what we could otherwise do with the error..)

#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct Theme {
    pub is_dark_mode: bool,
}

impl Store for Theme {
    fn new() -> Self {
        yewdux::storage::load(yewdux::storage::Area::Local)
            .map_err(|error| error!("Unable to load state due to StorageError: {}", error))
            .ok()
            .flatten()
            .unwrap_or_default()
    }

    fn changed(&mut self) {
        if let Some(error) = yewdux::storage::save(self, yewdux::storage::Area::Local).err() {
            error!("Unable to save state due to StorageError: {}", error)
        }
    }
}

I would assume that, at many times, nothing more sophisticated is required and therefore a default (or standard) implementation for the Store trait that provides just something like this should be worth it.
Havin the Store macro already, two additional macros named LocalPersistedStore and SessionPersistedStore would do it, letting the user choose the yewdux::storage::Area being used.
With a note at the documentation/readme that anything else can and must be implemented manually.

yewdux for yew-functional: yewdux-functional

There should be an official integration with the upcoming functional components. This should probably belong in another yewdux-functional crate because yew itself does not depend on yew-functional.

The API could simply be:
use_dispatch<T>() -> Dispatch<T> that subscribes to the store and automatically triggers an update when the store is modified

I would also like to propose making this repo a monorepo containing both yewdux and yewdux-functional like the yew repo.

I can attempt implementing this if it's ok.

Support wasm only functions via macro [doc-test]

I have a quiet complex development system, where rust analyzer is running over server/shared/frontend code. It would be helpful to enable wasm only feature via macro.
This has partially already been done here

It would be really helpful if it would be done everywhere, like here

yew version conflict?

I'm refactoring my yew app which is in a small state.

I have upgraded from yew = "0.17" -> yew = "0.18" (YEW) and yew-state = "0.4" -> yewdux = "0.6.1".

My relevant depends are:

yew = "0.18.0"
yew-router = "0.15.0"
yewdux = "0.6.1"
yewtil = "0.4"

I have used the example as my guideline but I'm getting this error:

error[E0308]: mismatched types
  --> frontend/src/components/account.rs:56:46
   |
56 |             dispatch: Dispatch::bridge_state(link.callback(Msg::State)),
   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `yew::callback::Callback`, found enum `yew::Callback`
   |
   = note: expected enum `yew::callback::Callback<Rc<_>>`
              found enum `yew::Callback<Rc<AppState>>`
   = note: perhaps two different versions of crate `yew` are being used?

error[E0282]: type annotations needed
  --> frontend/src/views/home/index.rs:49:22
   |
49 |                     .reduce_callback(|app_state| app_state.jwt = None);
   |                      ^^^^^^^^^^^^^^^ cannot infer type for type parameter `E` declared on the associated function `reduce_callback`
   |
help: consider specifying the type arguments in the method call
   |
49 |                     .reduce_callback::<E, impl Fn(&mut Model<Self::Store>) + 'static>(|app_state| app_state.jwt = None);
   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// account.rs
impl Component for Account {
    type Message = Msg;
    type Properties = Props;

    fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
        link.send_message(Msg::Initialize);

        Self {
            props,
            dispatch: Dispatch::bridge_state(link.callback(Msg::State)),
            state: Default::default(),
        }
    }
// index.rs
    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::Logout => {
                self.storage_local.remove("jwt");

                self.dispatch
                    .reduce_callback(|app_state| app_state.jwt = None);

                let mut router =
                    RouteAgentBridge::new(self.link.callback(Self::Message::Navigation));
                router.send(RouteRequest::ReplaceRoute(AppRoute::Login.into()));

                false
            }

            Msg::Navigation(_) => {
                log::info!("Update Msg::Navigation in index");
                true
            }

            Msg::State(state) => {
                self.state = state;
                true
            }
        }
    }

What is wrong with the code or is yewdux not yew 0.17 or 0.18 ready?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.