Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for scoped contexts #48

Merged
merged 23 commits into from
Dec 19, 2023
Merged

Add support for scoped contexts #48

merged 23 commits into from
Dec 19, 2023

Conversation

intendednull
Copy link
Owner

@intendednull intendednull commented Feb 23, 2023

Primarily important for SSR support, containing state to the scope of the app, instead of the thread.

For a basic example, you can now create a local context for your dispatch:

#[derive(Clone, PartialEq, Default, Store)]
struct Counter(u32);

let cx = yewdux::Context::new();
let dispatch = Dispatch::<Counter>::new(&cx);

Changes to one context are not reflected in any others:

let cx_1 = yewdux::Context::new();
let dispatch_1 = Dispatch::<Counter>::new(&cx_1);

let cx_2 = yewdux::Context::new();
let dispatch_2 = Dispatch::<Counter>::new(&cx_2);

dispatch_1.set(Counter(1));
dispatch_2.set(Counter(2));

assert_ne!(dispatch_1.get(), dispatch_2.get());

The default context for a dispatch is global (thread local).

let dispatch_1 = Dispatch::<Counter>::global();
// This is equivalent to above.
let dispatch_2 = Dispatch::<Counter>::new(&yewdux::Context::global());

dispatch_1.set(Counter(1));

assert_eq!(dispatch_1.get(), dispatch_2.get());

A root component is now available to provide context that is scoped to the app:

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

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

#[function_component]
fn Counter() -> Html {
    let (state, dispatch) = use_store::<State>();
    let onclick = dispatch.reduce_mut_callback(|state| state.count += 1);
    html! {
        <>
        <p>{ state.count }</p>
        <button {onclick}>{"+1"}</button>
        </>
    }
}

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

Hooks will detect the context provided by YewduxRoot. If no root is provided, it uses global by default.

@intendednull intendednull changed the title Add support for local contexts Add support for scoped contexts Feb 23, 2023
There are some serious pitfalls using global (thread local) state while
in SSR time. To avoid major security risks, I've added a strict panic if
this is attempted.

Also improve api clarity.
While this can be a security issue, there are some use cases where it's
perfectly viable to utilize thread-local state on the server. This is
a case where good documentation should help with the niche pitfall,
instead of forbidding it entirely.
@Roba1993
Copy link

Roba1993 commented Aug 4, 2023

I just went by this PR and I really like it and would also help me for specific cases. I could use them instead of the yew OOTB use_store hooks, which unfortunately have no reducer functions....

Ebenso I like the new feature I'm strongly argue to not change the actual api for the Dispatcher. This would break my code at 300+ places...
I mean this function:

let dispatch = Dispatch::<Counter>::new(&cx);

I would rather propose to keep the new function as it is and have an additional function like this:

let dispatch = Dispatch::<Counter>::ctx(&cx);

I also don't understand the benefit of an App vs Thread context. But this is definitely on me.

I hope, this feedback helps :)

Only wasm32 targets are allowed global context access. Accessing global
context in a multi-threaded environment is unsafe, and not allowed.
This does break a lot of code, but is way more descriptive and clear as
to what exactly is happening. Since we are still pre-release, breaking
changes are expected, and shouldn't inhibit improvements.
@intendednull
Copy link
Owner Author

@Roba1993 thanks for the feedback!

I like the new feature I'm strongly argue to not change the actual api for the Dispatcher. This would break my code at 300+ places...

Hopefully you can refactor easily with a little regex magic. We aren't 1.0 yet, so breaking changes are to be expected, and shouldn't get in the way of improving the API.

I think this new naming scheme makes it super clear to the user when exactly they are accessing global context. It's a topic that may introduce confusion to newcomers, and so vitally important we make it as easy to understand as possible.

Includes a workaround for rust-lang/rust#67295

Global context is only available for wasm, but tests are run natively.
Because #[cfg(doctest)] does not work as expected, we include a feature
to fill that role. We should eventually switch back over when the fix is
merged into cargo.
@Roba1993
Copy link

Hopefully you can refactor easily with a little regex magic. We aren't 1.0 yet, so breaking changes are to be expected, and shouldn't get in the way of improving the API.

I think this new naming scheme makes it super clear to the user when exactly they are accessing global context. It's a topic that may introduce confusion to newcomers, and so vitally important we make it as easy to understand as possible.

Yes, I will be able to do this 😄

@intendednull intendednull merged commit 730d418 into master Dec 19, 2023
@intendednull intendednull deleted the local-contexts branch December 19, 2023 01:41
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.

None yet

2 participants