RFC: Option to provide wrapping components to support Suspense boundaries #11152
Replies: 7 comments 7 replies
-
It was also raised in #11133 by @xotahal that a similar solution might be useful for providing context to a screen's header. The implementation in the PR does not address this use case effectively, however the proposal of a To provide context to a screen's header, the wrapping component slot would need to sit at a higher level, however this becomes problematic for stack navigators using the floating header which is not rendered per-screen but instead has a single header shared across multiple screens. In some cases wrapping the entire stack/navigator with a context provider would be desireable and sounds like something that could be well worth supporting, but I'll let @xotahal comment on wether that would be suitable for their particular use case. I think a wrapping component at the navigator level wouldn't be suitable for Suspense because it would mean the header and the previous screens in the stack would be affected by the suspense boundary, unless stack implemented its own To solve both use cases, there could be multiple options which provide the ability to insert a wrapping component at different levels of the stack (or other) navigator. This could be a potential foot-gun as it requires somewhat deep understanding of the internal implementation of the navigator around which level in the hierarchy is most appropriate for each use case. |
Beta Was this translation helpful? Give feedback.
-
Ok. Let me try to describe our needs. Let's say that we have To summarize our goal in points:
We don't need a separate wrapper for the header and content since you can achieve it by simply wrapping those in the render function. However, I understand the reason why @levibuzolic proposing the change. It definitely simplifies the code. 👏 I don't have enough knowledge about the code of this library so I can't suggest where we should inject this context. But let me try to "think out loud" about possible changes: const DataComponent = (props) => {
const item = useStore(props.route.params.itemId) // goes to suspense if no data
// this might be an options hot set options and params?
// props.navigation.setOptions({ ... })
// props.navigation.setParams({ ... })
return props.children
}
const ScreenWrapperComponent = (props) => {
return (
<Suspense>
<StoreProvider><DataComponent /></StoreProvider>
</Suspense>
)
}
<Screen
wrappers={() => ({
screen: ScreenWrapperComponent, // this wraps both component and header
component: Suspense, // this wraps only Component
header: null, // wraps only Header
})}
options={(params) => {
const item = useStore(params.itemId)
return { ... }
}}
// this should be merged with initialParams that are static and used in the screen later
dynamicParams={(initialParams) => {
const item = useStore(params.itemId)
return { ... }
}}
/> |
Beta Was this translation helpful? Give feedback.
-
Thanks for the RFC. I currently don't have much time to focus on this so I just wanted to lay down what I have in mind for suspense and data-fetching support in React Navigation. Each
These are just my initial ideas. I'd like to spend some time learning more about how tools such as Remix handle this as well before settling on an approach. Maybe a wrapper prop (or Note that these don't go in Can you describe a bit more about your use case @xotahal - especially why do you need After I thought a bit more, I think not suspending UI outside the screens such as header and tab bar is probably not a big deal. For tabs, they need to be displayed regardless of whether the screen has loaded or not. Stack header can be hidden until there's data. One reason I'm hesitant about a This prop would enable a lot of flexibility that was not possible before, such as adding persistent UI elements:
If the navigator has this prop, having a very similar prop for screens would be confusing, especially when both do very different yet similar things. |
Beta Was this translation helpful? Give feedback.
-
I'm in the same situation as @MaxAst , trying to make React Navigation work with Relay. The main obstacle is passing loaded data from the Navigator to the Screen. I've scribbled together a visualization of how the render-as-you-fetch pattern works with Relay and how it maps (or fails to map) to React Navigation to give you some background. From what I've understood from @satya164 's post, the three proposed props could solve all three challenges described in my graphic, assuming that the loader is fired upon a navigation event (and can therefore execute Relay's loadQuery() and can then pass data (in this case Relay's non-serializable queryReference) to the screen component. |
Beta Was this translation helpful? Give feedback.
-
@satya164 regarding lack of time, is there any way non-maintainers can support you with this? |
Beta Was this translation helpful? Give feedback.
-
@EvanBacon First off, ExpoRouter is amazing. So excited for v2. I'd like to add a comment here that I think much of the React community really doesn't understand the full benefits and motivations all of the hard work Meta has been investing in React in terms of the suspense API. There's really a lot of potential to decrease loading times and give the developer control over the perceived loading time. Really, most of the large changes to React have bene built around Suspense (with the Relay framework they mention above being the flagship use-case). A modern react-focused router should absolutely be suspense compatible. This is this biggest omission of React Navigation. I wonder if really it needs to be added there first? You can see all the references to suspense-integrated routing in the react docs here: |
Beta Was this translation helpful? Give feedback.
-
React Navigation 7 will have |
Beta Was this translation helpful? Give feedback.
-
For apps that use
React.Suspense
(Relay, React Query etc.) and/or error boundaries for each screen it can be awkard to wrap every screen with the required components in userland.Problem example
Given the following screen and suspense/error boundary wrappers:
There are a few user-land options for handling wrapping every screen:
Screen.children
This opts out of the
React.PureComponent
optimisations of React Navigation and becomes quite verbose with many routes.Example
Manually wrapping
This keeps the optimisations, but still painful with many routes.
Example
Component factory
This is the currently the best user-land solution, but if we had support for providing a wrapper at the navigator level, this could be much simpler.
Example
Proposed solution
A
wrapper
option added to each navigator which allows the user to supply a component to wrap the content of the screen. This allows consistent behaviour across an entire navigator in addition to flexibility to alter the behaviour for each screen should that be required.Beta Was this translation helpful? Give feedback.
All reactions