crimx/observable-hooks

Question: best practice for integrating observable hooks into error boundaries and suspense

sirnewton01 opened this issue · 8 comments

Hello,

I'm new to React but have some experience with RxJS. The answers here might be self evident to those with more experience. I'll ask anyway because the intersection of the two doesn't seem to be very common yet.

In this example, different React components are piped depending on the state of the observable:
https://observable-hooks.js.org/examples/#conditional-rendering-vanilla-javascript

There is one JSX block that represents a loading state, another a steady state (with a value) and then there's also an error JSX in case of error. This looks really great if the component is managing each of these states and I find it nice and readable there in an RxJS sense.

What happens if the component is deep in a hierarchy of components and it doesn't handle the loading and/or error boundaries? I'm assuming that it wouldn't have any startsWith() or catchError() handlers in the pipe. But then, how will the parent components get triggered for suspense or error boundaries?

Is there a recommended way of handling this case?

Thank you,
Chris

crimx commented

Hi Chris,

If the Component doesn't handle loading or error state then you don't need a stream of React elements. Create a stream of values instead.

But then, how will the parent components get triggered for suspense or error boundaries?

For Suspense use use-suspensible on the emitted values.

For error boundaries I think you raised a very good question. Currently useSubscription and useObservableState (which uses useSubscription under the hood) will silently swallow the error by default. With useSubscription you can throw the error in the error callback. It is not possible now to do the same with useObservableState.

I think to support error boundaries useSubscription should throw the error by default if no callback is provided.

crimx commented

After some digging I just realized that this is not what error boundaries are for. Error boundaries should be used for preventing uncontrollable render errors on children breaking the Component, not for intentional conditional rendering.

The ErrorUI in the example represents the error of the logic not the Component rendering. It is part of the failed state.

I think the current error handling of useSubscription and useObservableState is the right behavior.

Thank you @crimx for the background on all of this.

So, if I understand correctly, error boundaries probably don't relate at all to what happens in the observable pipelines since they are failure state, and not rendering error state.

When trying to leverage suspense, just use streams of values and hook them into the usual suspense mechanism or with the use-suspense.

Do you think that there's much difference in terms of performance using streams of React elements compared conditional rendering? A colleague of mine raised a concern about the implications of virtual DOM in the context of observable pipeline. I didn't think that there would be much difference from conditional rendering, but perhaps I'm not understanding the way that this all works in React.

crimx commented

So, if I understand correctly, error boundaries probably don't relate at all to what happens in the observable pipelines since they are failure state, and not rendering error state.

Yes. Even though the docs says "It would catch both rendering errors and errors from Suspense data fetching." which sounds like a feature, I would say it is more of a limitation. The data fetching promise is thrown to Suspense. If you want to handle error inside Suspense you'll have to catch the error beforehand and return something like { result, error } which defeats the purpose of Suspense.

This can easily lead to wrapper hell. But since Suspense is still experimental, the React team may come up with other ways to handle this in the future.

Nevertheless, this is the only option on the docs right now. I should explore the possibilities of better intergration for folks who want to adopt the pattern.

A colleague of mine raised a concern about the implications of virtual DOM in the context of observable pipeline.

React elements are just objects. Observable emitted elements are no different than the normal elements. In fact the Observable pipeline is like useMemo on elements. It will be faster on re-rendering when the element does not need to update. This could happen a lot in function components.

crimx commented

Just added first class Suspense support. If interested you may play the suspense example on suspense branch.

git clone git@github.com:crimx/observable-hooks.git --branch suspense
cd observable-hooks
yarn install
yarn build
cd examples/suspense
yarn start
crimx commented

Finally finish testing and writing docs. v2.2.0 is released! See Render-as-You-Fetch (using Suspense).

Thanks @crimx I'm reading and learning from the example now. I can close this issue if you'd like.

crimx commented

Sure. You may reopen if you think the issue needs further discussion.