yewstack / yew

Rust / Wasm framework for creating reliable and efficient web applications
https://yew.rs
Apache License 2.0
30.77k stars 1.43k forks source link

Unclear how to update a Context only with Components #2923

Closed sasacocic closed 1 year ago

sasacocic commented 2 years ago

This is about:

Problem AFAICT there isn't any documentation showing how you can use a context (create a context and consume it appropriately to update your component) that doesn't use function components.

e.g. In the Contexts documentation on yew.rs it doesn't show how to update a Context and have it reflected.

Details about the solution you'd like (Optional)

Just add to the existing documentation for https://yew.rs/docs/concepts/contexts - by including another section called updating context - it could simply show how you can update the context and have it reflected in your component.

Additional context (Optional)

https://github.com/yewstack/yew/blob/master/examples/contexts/src/struct_component_subscriber.rs <- shows a good example of how to do this, although it's unclear why you need to store a ContextHandle<_>

Questionaire (Optional)

WorldSEnder commented 2 years ago

There are two parts for contexts to work: A "provider" somewhere up in the virtual dom tree, and the "consumers" below it.

When a provider wants to make some type of context, let's say a Theme, a value theme: Theme accessible to some of its children, he should render

<ContextProvider<Theme> context={theme}>
    {children_with_consumers}
</ContextProvider<Theme>>

To update the provided value, the provider needs to re-render and pass the updated value as the new context property of <ContextProvider<Theme> context={theme}>. This will then trigger all subscribers down below. I think at this point the current page is inaccurate saying

The children are re-rendered when the context changes.

instead of

The subscribed children are notified when the context changes.

Note that children_with_consumers does most often not change in this re-render, so not all children are re-rendered. But it is technically allowed to mix these two updates and when new, changed, children_with_consumers are rendered, this will re-render all of those (shallowly, with the usual vdom diffing) too. I'm a bit split if that point is worth mentioning, since it's more-or-less the only sane and also expected behavior.

The struct consumer calls ctx.link().context::<Theme>(cb) and has to provide a callback cb that will be called when the context changes (the trigger). This callback is called with the updated context value and is expected to return a Message that is sent to the struct component. The call to context returns the current/initial, immediate context value (as the provider already exists and has a definite provided value) and a ContextHandle. This second struct models the interest in receiving updates through the callback. Dropping it (e.g. as part of the component being destroyed), will unsubscribe from receiving updates when the context updates.

The struct example (in the v0.19 docs) slightly misused this API in the guise of simplification. The context is gotten directly in fn view, which always uses the most up-to-date context either way, which does more work and doesn't receive updates - passing Callback::noop(). This will fail to re-render the component when the the theme changes.

In function components, use_context is similar, except its use will always re-render instead of sending a message, and the updated context will be picked up during that render, so no callback here. An example is missing and should also show off using the Theme context. I.e. the current setup shows "2 steps", but mixes the provider and a short version of a consumer. It would make sense to actually show how prop-drilling is avoided instead.

sasacocic commented 1 year ago

So taking a look at the next documentation it looks like this was mostly addressed. When is next expected to be released? Since that change has been made I think it's fine to close this issue.

ranile commented 1 year ago

Closing this as Yew 0.21 has been released with the fix