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 use_context hook #1057

Closed
jstarry opened this issue Mar 28, 2020 · 17 comments · Fixed by #1249
Closed

Add support for use_context hook #1057

jstarry opened this issue Mar 28, 2020 · 17 comments · Fixed by #1249
Labels
feature-request A feature request

Comments

@jstarry
Copy link
Member

jstarry commented Mar 28, 2020

I'm most excited for use_context to be used in Yew! I think it should be possible to make providers using nested components. I imagine we will match React's implementation pretty closely: https://reactjs.org/docs/hooks-reference.html#usecontext

@jstarry jstarry added the feature-request A feature request label Mar 28, 2020
@jstarry
Copy link
Member Author

jstarry commented Mar 28, 2020

@ZainlessBrombie do you want to take this one?

@ZainlessBrombie
Copy link
Collaborator

Sure :)
In react, the context object used is created using the createContext method. This then returns the context object which is exported and later imported by other modules.
I think it would make sense to instead accept a generic type as the context, would you agree?

@ZainlessBrombie
Copy link
Collaborator

ZainlessBrombie commented Mar 29, 2020

Also I'm gonna have to use a HashMap<TypeId, Other> in there somewhere to keep track of the current Context for each context type 🤔

@jstarry
Copy link
Member Author

jstarry commented Mar 29, 2020

I think it would make sense to instead accept a generic type as the context, would you agree?

Sounds reasonable, I haven't thought about the implementation too much yet

Also I'm gonna have to use a HashMap<TypeId, Other> in there somewhere to keep track of the current Context for each context type 🤔

I wonder if TypeId is enough as a key though... you could have two different contexts with the same type in an app. Maybe it could work as a stack and the providers push context onto the stack for a particular type?

@jstarry
Copy link
Member Author

jstarry commented Mar 29, 2020

I'm not sure how contexts would be popped off the stack though.. maybe we do need an updated lifecycle method for Component after all

@ZainlessBrombie
Copy link
Collaborator

I wonder if TypeId is enough as a key though... you could have two different contexts with the same type in an app. Maybe it could work as a stack and the providers push context onto the stack for a particular type?

Yes thats What I meant, we need to be able to nest contexts and have them run in parallel, the map is just a part of it :)

@jstarry
Copy link
Member Author

jstarry commented Mar 29, 2020

what do you mean by have them run in parallel?

@ZainlessBrombie
Copy link
Collaborator

ZainlessBrombie commented Mar 29, 2020

Bad way to put it, my bad - I meant to have two context providers next to each other like

<div>
  <ContextProvider context=context_of_type_a>
    <SomeContextUser />
  </ContextProvider>
  <ContextProvider context=other_context_of_type_a>
    <SomeContextUser />
  </ContextProvider>
</div>

@jstarry
Copy link
Member Author

jstarry commented Mar 29, 2020

Ah yeah, understood 👍

So do you think we will need Component::updated()?

@ZainlessBrombie
Copy link
Collaborator

I'm not sure how contexts would be popped off the stack though.. maybe we do need an updated lifecycle method for Component after all

Don't think so, they would be pushed onto the stack before the function renders and popped off after it is done rendering, both in the view method

@jstarry
Copy link
Member Author

jstarry commented Mar 29, 2020

Don't think so, they would be pushed onto the stack before the function renders and popped off after it is done rendering, both in the view method

Ah, right! Never mind 😄

@ZainlessBrombie
Copy link
Collaborator

ZainlessBrombie commented Mar 29, 2020

Actually you were correct. We do need a method that runs after a component was rendered. More specifically we need:

<div>
  <ContextProvider context=A>
    <ContextReceiver /> // will receive a context. view called on this after ContextProvider.view
    // but before ContextProvider.updated
  </ContextProvider>
  <ContextReceiver /> // will *not* receive a context, it's view method would need to be called after
  // ContextProvider received updated method. 
</div>

@ZainlessBrombie
Copy link
Collaborator

ZainlessBrombie commented Mar 29, 2020

I would do that myself but it seems to me that it's 20% code and 80% knowing where to place it 😬
If you have the time that or pointers would be appreciated :)
Also PS I just now learned about the whole actix-web drama so I just wanna say thank you for maintaining this so cooperatively and investing your time!

@jstarry
Copy link
Member Author

jstarry commented Mar 30, 2020

Actually you were correct. We do need a method that runs after a component was rendered

I would do that myself but it seems to me that it's 20% code and 80% knowing where to place it 😬

Ok! I'll add that as soon as I can, I think I have a branch laying around still. Tricky part is the scheduler :)

Also PS I just now learned about the whole actix-web drama so I just wanna say thank you for maintaining this so cooperatively and investing your time!

Of course! Yew has very little drama so far, hope it stays that way! It's a lot of fun to work together with the community on things

@jstarry
Copy link
Member Author

jstarry commented Apr 27, 2020

@ZainlessBrombie I just created #1151 which should unblock context hooks!

@siku2
Copy link
Member

siku2 commented May 20, 2020

So I started looking into this issue.
I merged the current master branch with @ZainlessBrombie's progress. You can find that here.

Currently I'm considering a bit of a different approach though. Instead of maintaining a stack of contexts, which doesn't work out as nicely because of the rendering order and all that, I'm thinking, use_context should just recursively traverse the parents to find the nearest context provider with the correct type.
Thanks to #1151 this is essentially just traversing a linked list so the performance shouldn't be bad.

The problem I'm having is that in order to do this use_context needs to have access to the component's scope somehow. Having the user pass it to the function isn't really an option in my opinion (FunctionComponents can't even do that right now).

From what I can tell there's no way to grab the scope for the current context yet

One option would be to add a use_scope hook which could be used by use_context internally.
Since use_context isn't really a hook at all this doesn't violate the "no nested hooks" limitation.
This hook would rely on a thread-local variable which is maintained by the FunctionComponent much like CURRENT_HOOK.

Does this sound reasonable in any way or am I completely off here?

Either way it seems to me that use_context needs to have access to the component's scope. I can't think of another way to avoid the siblings receive context even though they shouldn't issue.

@jstarry
Copy link
Member Author

jstarry commented May 20, 2020

@siku2 sounds reasonable to me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request A feature request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants