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 use_future hook to make consuming futures as suspense easier #2609

Merged
merged 5 commits into from Apr 14, 2022

Conversation

hamza1311
Copy link
Member

@hamza1311 hamza1311 commented Apr 13, 2022

Description

This PR introduced a new hook, use_future, to allow suspending a future and resuming with it's response

Checklist

  • I have run cargo make pr-flow
  • I have reviewed my own code
  • I have added tests

@hamza1311 hamza1311 added ergonomics A-yew Area: The main yew crate labels Apr 13, 2022
@hamza1311 hamza1311 added this to the v0.20 milestone Apr 13, 2022
github-actions[bot]
github-actions bot previously approved these changes Apr 13, 2022
#[hook]
pub fn use_suspending_future<T, F>(f: F) -> SuspensionResult<T>
where
T: Clone + 'static,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any way to avoid this Clone bound?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about Rc<T>?

github-actions[bot]
github-actions bot previously approved these changes Apr 13, 2022
@github-actions
Copy link

github-actions bot commented Apr 13, 2022

Visit the preview URL for this PR (updated for commit 48eb65f):

https://yew-rs-api--pr2609-use-suspending-futur-rf59mav0.web.app

(expires Thu, 21 Apr 2022 17:18:59 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

@github-actions
Copy link

github-actions bot commented Apr 13, 2022

Size Comparison

examples master (KB) pull request (KB) diff
boids 173.304 173.304 0
contexts 111.400 111.400 0
counter 86.998 86.998 0
counter_functional 87.475 87.475 0
dyn_create_destroy_apps 90.104 90.104 0
file_upload 102.928 102.928 0
function_memory_game 170.001 170.001 0
function_router 351.576 351.576 0
function_todomvc 161.881 161.881 0
futures 226.619 226.619 0
game_of_life 108.005 108.005 0
inner_html 83.505 83.505 0
js_callback 92.101 92.101 0
keyed_list 195.862 195.862 0
mount_point 86.512 86.512 0
nested_list 115.576 115.576 0
node_refs 89.835 89.835 0
password_strength 1539.503 1539.503 0
portals 96.908 96.908 0
router 316.636 316.636 0
simple_ssr 499.811 499.811 0
ssr_router 425.250 425.250 0
suspense 110.382 110.382 0
timer 89.713 89.713 0
todomvc 143.691 143.691 0
two_apps 87.583 87.583 0
webgl 87.123 87.123 0

@WorldSEnder
Copy link
Member

What's your opinion on calling it simply use_future?

@hamza1311
Copy link
Member Author

What's your opinion on calling it simply use_future?

That's a good idea, especially since it being in yew::suspense covers the suspending part

github-actions[bot]
github-actions bot previously approved these changes Apr 13, 2022
@futursolo
Copy link
Member

futursolo commented Apr 14, 2022

Sorry, I might not be able to review any pull request in detail until this weekend.

For a memorised task (future), you may want to associate the task with a dependent type and rerun the task when the dependency changes and provide a method on the handle type to rerun the task when needed.

#[hook]
pub fn use_suspending_future<F, T, O, D>(f: F, deps: D) -> SuspensionResult<UseSuspendingFutureHandle<O>>
where
    F: FnOnce(&D) -> T,
    T: 'static + Future<Output = O>
    D: 'static + PartialEq
    O: 'static
{
    todo!()
}

I will explain this in detail #2584 this weekend.
If you wish to not hold this pull request until the weekend, please proceed with merge after approval from others.

Copy link
Member

@WorldSEnder WorldSEnder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the hook, a lot. This should make it very easy to integrate data from a web requests into the component.

packages/yew/src/suspense/hooks.rs Outdated Show resolved Hide resolved
packages/yew/tests/suspense.rs Show resolved Hide resolved
@hamza1311
Copy link
Member Author

I tried implementing use_future_with_deps but couldn't because of lifetime errors. use_memo doesn't like to work here? Here's the code and error, in case anyone knows a solution:

Code
    #[hook]
    pub fn use_future_with_deps<F, D, T, O>(f: F, deps: D) -> SuspensionResult<UseFutureHandle<O>>
    where
        F: FnOnce(&D) -> T + 'static,
        D: PartialEq + 'static,
        T: Future<Output = O> + 'static,
        O: 'static,
    {
        let output = use_state(|| None);

        let suspension = {
            let output = output.clone();

            use_memo(
                move |deps| {
                    Suspension::from_future(async move {
                        output.set(Some(f(deps).await));
                    })
                },
                deps,
            )
        };

        if suspension.resumed() {
            Ok(UseFutureHandle { inner: output })
        } else {
            Err((*suspension).clone())
        }
    }
Error
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
   --> packages/yew/src/suspense/hooks.rs:50:56
    |
50  |                       Suspension::from_future(async move {
    |  ________________________________________________________^
51  | |                         output.set(Some(f(deps).await));
52  | |                     })
    | |_____________________^
    |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
   --> packages/yew/src/suspense/hooks.rs:49:17
    |
49  | /                 move |deps| {
50  | |                     Suspension::from_future(async move {
51  | |                         output.set(Some(f(deps).await));
52  | |                     })
53  | |                 },
    | |_________________^
note: ...so that the types are compatible
   --> packages/yew/src/suspense/hooks.rs:50:56
    |
50  |                       Suspension::from_future(async move {
    |  ________________________________________________________^
51  | |                         output.set(Some(f(deps).await));
52  | |                     })
    | |_____________________^
    = note: expected `(functional::hooks::use_state::UseStateHandle<_>, _, &D)`
               found `(functional::hooks::use_state::UseStateHandle<_>, _, &D)`
    = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `impl std::future::Future<Output = [async output]>` will meet its required lifetime bounds...
   --> packages/yew/src/suspense/hooks.rs:50:21
    |
50  |                     Suspension::from_future(async move {
    |                     ^^^^^^^^^^^^^^^^^^^^^^^
note: ...that is required by this bound
   --> packages/yew/src/suspense/suspension.rs:137:58
    |
137 |         pub fn from_future(f: impl Future<Output = ()> + 'static) -> Self {
    |                                                          ^^^^^^^

@hamza1311 hamza1311 changed the title Add use_suspending_future hook to make consuming futures as suspense easier Add use_future hook to make consuming futures as suspense easier Apr 14, 2022
@hamza1311 hamza1311 merged commit edeb59d into yewstack:master Apr 14, 2022
@jetli
Copy link
Contributor

jetli commented Apr 15, 2022

            let suspension = {
            let output = output.clone();

            use_memo(
                move |deps| {
                    Suspension::from_future(async move {
                        output.set(Some(f(deps).await));
                    })
                },
                deps,
            )
        };

@hamza1311 you might clone output one more time, because futures needs static life time, you need to clone everything it consumes, like:

        let suspension = {
            let output = output.clone();

            use_memo(
                move |deps| {
                    let output = output.clone(); # <-- here
                    Suspension::from_future(async move {
                        output.set(Some(f(deps).await));
                    })
                },
                deps,
            )
        };

@hamza1311
Copy link
Member Author

@jetli I tried that, it ends up with the same error. I think it has something to do with deps not being 'static in the future, which I'm not sure how to solve

@WorldSEnder WorldSEnder mentioned this pull request Apr 15, 2022
3 tasks
@jetli
Copy link
Contributor

jetli commented Apr 15, 2022

@hamza1311 yes, deps needs to be cloned too, maybe wrap it in Rc::new, like use_effect_with_deps does.

@jetli
Copy link
Contributor

jetli commented Apr 15, 2022

this compiles:

    #[hook]
    pub fn use_future_with_deps<F, D, T, O>(f: F, deps: D) -> SuspensionResult<UseFutureHandle<O>>
    where
        F: FnOnce(&D) -> T + 'static,
        D: PartialEq + 'static,
        T: Future<Output = O> + 'static,
        O: 'static,
    {
        let output = use_state(|| None);

        let suspension = {
            let output = output.clone();

            use_memo(
                move |deps| {
                    let f = f(deps);
                    Suspension::from_future(async move {
                        output.set(Some(f.await));
                    })
                },
                deps,
            )
        };

        if suspension.resumed() {
            Ok(UseFutureHandle { inner: output })
        } else {
            Err((*suspension).clone())
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-yew Area: The main yew crate ergonomics
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants