A nice use-case for React Native Portals
ome days ago we encountered a problem that seemed very time consuming to resolve it.
We've had the following components structure.
export const Page = () => {
return (
<View>
<TopHeaderComponent/>
<PageContent />
</View>
);
};
export const Popup = () => {
return (
<Popup>
<ScrollView stickyHeaderIndices=[0]>
<Header/>
<Page/>
</ScrollView>
</Popup>
);
};
The new requirement was that the <TopHeaderComponent/>
should also be sticky and the initial idea was to move that code from <Page/>
into <Header/>
component, because that one was already sticky.
This was not so easy to do because that <TopHeaderComponent/>
was doing a lot of logic with other components from <PageContent/>
and to extract that logic would be implied to add a new set of Props and React.Context values to be able to communicate with the <Page/>
.
Here is where the portals idea seemed a good solution, basically it's a way to keep the code inside <Page/>
component but you can teleport the view outside of <Page/>
component in the <Header/>
component.
To able to achieve this we added react-native-portal, this works well with React Native and React Native Web.
Here is an example of how to use the library:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Portal, PortalProvider, PortalHost } from '@gorhom/portal';
const BasicScreen = () => {
return (
<View style={styles.container}>
<Portal hostName="hostKey">
<Text>
Text to be teleported to the custom host
</Text>
</Portal>
</View>
);
};
const OtherScreen = () => {
return (
<View>
<Stuff/>
<PortalHost name="hostKey" />
/* Here will go that Text component */
</View>
);
}
export default () => (
<PortalProvider>
<NavigationContainer>
<BasicScreen/>
<OtherSceen/>
</NavigationContainer>
</PortalProvider>
);
Based on this example our new components structure now look like this:
export const Popup = () => {
return (
<Popup>
<ScrollView stickyHeaderIndices=[0]>
<View>
<Header/>
<PortalHost name="headerHostKey" />
</View>
<Page/>
</ScrollView>
</Popup>
)
};
and in <Page/>
component we have this:
export const Page = () => {
return (
<View>
<Portal hostName="headerHostKey">
<TopHeaderComponent/>
</Portal>
<PageContent />
</View>
);
};
Don't forget to wrap your app with <PortalProvider/>
, this component will search in the Virtual DOM for all the active portals.
That was it, a few lines of code saved us days of refactoring!