Open quangphuchuynh95 opened 1 year ago
Why are you wrapping PageAContainer
with NiceModal.Provider
again? Didn't you already wrap your entire app with NiceModal.Provider
in index.js
?
Just remove NiceModal.Provider
from PageAContainer
, and all cases should work.
@alexandredev3 That's a good question. Let me explain a bit deeper in my case. I have a code example here.
My app structure:
<ThemeProvider value={globalTheme}>
<NiceModal.Provider>
// some other levels of children
<ThemeProvider value={localTheme}>
<MyComponent />
</ThemeProvider>
// ...
</NiceMpdal.Provider>
</ThemeProvider>
MyComponent:
// ...
NiceModal.show(ModalComponent)
return ...
ModalComponent:
// ...
const theme = useTheme()
// ...
Here it will return globalTheme
instead of localTheme
, even though <MyComponent />
is mounted inside the <ThemeProvider value={localTheme}>
I understand because ModalComponent
is mounted at a <PlaceHolder>
at the same level of <NiceModal.Provider>
So I need to add another <NiceModal.Provider>
inside <ThemeProvider value={localTheme}>
, to make the localTheme
accessible from <ModalComponent />
Well, if I'm not wrong, you can't have a NiceModal.Provider
and wrap its children with another NiceModal.Provider
. Probably because the API gets lost between the NiceModal providers, and there's no way to say if the show
method that you're calling is from the first Provider or the second one. To fix that, you'd have to work around this by isolating the first NiceModal.Provider
and all its children and doing the same with the second NiceModal.Provider
and all its children.
Something like this:
<ThemeProvider value={globalTheme}>
// First NiceModal Provider
<NiceModal.Provider>
<ComponentChildren />
</NiceModal.Provider>
// Second NiceModal Provider
<NiceModal.Provider>
<ThemeProvider value={localTheme}>
<MyComponent />
</ThemeProvider>
</NiceModal.Provider>
</ThemeProvider>
I have a suggestion about how to support nested NiceModal Provider:
Give NiceModal.Provider a optional user defined id prop, and user can declare which provider the show
method should render modal to, something like this:
<ThemeProvider value={globalTheme}>
// First NiceModal Provider, id is "default", it will use globalTheme
<NiceModal.Provider>
<ComponentChildren />
// Second NiceModal Provider, id is "local", it will use localTheme
<ThemeProvider value={localTheme}>
<NiceModal.Provider id="local">
<MyComponent />
</NiceModal.Provider>
</ThemeProvider>
</NiceModal.Provider>
</ThemeProvider>
// static API to show
NiceModal.show(CustomModal, {
provider: 'local', // if not set, it will use "default"
// other options
})
If want automatic use nearest provider, use a new hooks to wrap it
const provider = NiceModal.useProvider()
// static API
NiceModal.show(CustomModal, {
provider: provider.id,
// other options
})
// hooks API
provider.show(CustomModal)
Thanks @kainstar ! I was thinking about a similar solution (not tried yet), use some concept like mountNode
or ModalPlaceHolder
where modal is rendered.
Something like,
const mountNode = useRef()
NiceModal.show(modal, props, mountNode.current)
...
The benefit is no need for nested wrapper provider. The disadvantage is the holder can only be defined where modal is used. It's very like using declarative way.
So I've not decided it.
mountNode
sounds like specifying the rendering position of the modal in the actual DOM tree. In my opinion, we write nested ModalProvider is because we want it to use different context value, such as theme configuration. The rendering position of the modal component in the DOM tree is always implemented by UI libraries.
The ModalPlaceholder
sounds similar to antd's useModal, which specifies where the modal component is rendered in React's virtual DOM tree and allows it to access all context information at that location. However, when writing big application, it's boring to write template code to obtain and render placeholders for each component that would renders modals. Provide a solution to use placeholder in parent component is more efficient (like antd's useApp).
Overall, I believe ModalPlaceholder
might be a better option, and it would be even better if official support nested providers.
Steps to reproduce
Case 1:
Case 2:
Github repository
https://github.com/quangphuchuynh95/test-nice-modal
Codesandbox
https://codesandbox.io/s/wizardly-goldberg-rp3vtz?file=/src/index.js