Nested component in Portal not receiving state values of React Context
Current behaviour
When using Portal component you are not given the context of the state in the wrapping/global React Context Provider
Expected behaviour
You should be able to access the state values of the Context Provider
How to reproduce?
- create a new expo app
- install react-native-paper
- create a context using createContext from react and give it an initial state/default value
- wrap the entire app in your named context.Provider
- create any component (text, view, snackbar etc.)
- using the Portal component wrap the created component
- inside the created component call the useContext() with your created context as the parameter
- expect the state to be defined/a value, but it is undefined
https://snack.expo.dev/184285Vsi - here is a simple snack to what the issue is, with some logs
Preview
The Context and Provider
Main App wrapped in Provider
Portal Component
nested component of Portal
Log of initial values outside of Portal
We expect these values to be the same within the portal
However as seen the in screenshot of the logs below it is undefined:
What have you tried so far?
To fix this I have passed in the state values as props to the nested component - this seems to work, however it is defeating the purpose of using Context Providers of react
Your Environment
| software | version |
|---|---|
| react-native | 0.71.7 |
| react-native-paper | ^5.7.0 |
| node | 16.19.0 |
| npm | 8.19.3 |
| expo sdk | ^48.0.11 |
Thanks for raising this issue! We're experiencing the same issue - took us days to debug.
Try flipping the positions of <StateProvider> and <Provider>
Try flipping the positions of
<StateProvider>and<Provider>
But then we won't get access to the theme in our StateProvider will we? Which might be a bit of a pain as I can imagine things like error boundaries, notifications, snackbars, etc. might be needed outside the StateProvider
I still fell like this is breaking the React rule. If something is inside a React context, it should have access to that context - where as that is not the case with the RNP Portal component.
And the fix of simply moving the state context outside of the theme context seems like a hack, as now we'll not have access to the theme inside the state provider.
Anybody have a reason as to why this occurs?
@ismailcodeboi I tinkered a bit with your example and seems that the <Portal> component will always render outside of the parent tree, while <Portal.Host> is the one that renders the <Modal> alongside others components in the parent tree and can access the StateContext.
Not really sure if it should behave this way, but the children will be rendered only inside of these providers: (see line 59)
https://github.com/callstack/react-native-paper/blob/1ce31113cd67e7aa450c72d6f86cd4a20c8157a1/src/components/Portal/Portal.tsx#L44-L68
One big problem that we currently have with this issue, is that our modals cannot read the navigation state or use the navigation hooks. We're using React Navigation for our navigation, which requires routes at the root level of it context wrapper - meaning that we can't have our theme context nor our own state context inside our navigation context. This means that we won't be able to access these contexts inside our modals, which is quite frustrating as we can't have any navigation functionality on our modals now.
I'm facing the same problem thank you for open this issue. You guys have any elegant workaround for this?
I'm facing the same problem thank you for open this issue. You guys have any elegant workaround for this?
Unfortunately not an elegant solution, but we ended up wrapping our RNP ThemeProvider in our state providers. This did require a bit of rework of our application. We had to move our theme and state consumers inside the theme provider (which is inside our state providers). This then allowed all Portal components to have access to the relevant context.
This is still not an ideal solution, as our NavigationProvider (from React Navigation) cannot be outside the ThemeProvider due to the way it works. This means that modal components cannot consume the navigation context (navigation state and navigation functions).
Yeah, I ended up doing the same and facing the same problem of not being able to use anything from React Navigation.
I'll try some workarounds (like I've being doing) and when they fix, maybe, I'll change this "temporary code", but thank you anyway buddy
I still fell like this is breaking the React rule. If something is inside a React context, it should have access to that context - where as that is not the case with the RNP
Portalcomponent.And the fix of simply moving the state context outside of the theme context seems like a hack, as now we'll not have access to the theme inside the state provider.
Totally agree. Also took hours to find out. And the same happens with React Navigation context as reported in #4022 .
I'm also facing the same problem.
+1, facing the same issue. Is there an update for this?
Any idea for how to attract some attention from RN paper devs? This issue seems abandoned. Would be petty to make a MR with something they don't approve
Also note that you can use Portal.Host so that in theory you would be able to access the contexts by creating the instance after creating the contexts, however this doesn't appear to work as expected
import { useState } from 'react';
import { Provider, Portal } from 'react-native-paper';
import { StateProvider } from './StateProvider.jsx';
import { CustomPage } from './CustomPage.jsx';
import { CustomModal } from './CustomMOdal.jsx';
export default function App() {
const [showModal, setShowModal] = useState(false);
return (
<Provider>
<StateProvider>
<Portal.Host>
<CustomPage show={showModal} setIt={setShowModal}></CustomPage>
<CustomModal
show={showModal}
onClose={() => setShowModal(false)}></CustomModal>
</Portal.Host>
</StateProvider>
</Provider>
);
}
Anyways, this is my hacky workaround:
import {ThemeContext} from '@shared/assets/themes/themeContext';
import React, {useContext} from 'react';
import {Portal as PaperPortal} from 'react-native-paper';
function PortalWithTheme(props: {children: React.ReactNode}) {
const themeContext = useContext(ThemeContext);
const theme = themeContext.theme;
const setTheme = themeContext.setTheme;
return (
<PaperPortal {...props}>
<ThemeContext.Provider value={{theme: theme, setTheme: setTheme}}>
{props.children}
</ThemeContext.Provider>
</PaperPortal>
);
}
export default PortalWithTheme;
Basically just redefine the context within the portal, and it doesn't conflict with the context outside the portal