To use Material-ui dialog
component, the official way is to add the dialog
component to the component where you would open or close it by setting a open
flag to true
or false
. The problem with this approach is that React will always render the modal. If the user never opens the modal, it is a waste. This is not a problem we should worry about until we have rarely used and expensive modal. For example, a modal uses a hook to get a large list of data and requires some computation. The computation will take place every time, even if the modal is never opened. This is a good use case for a Dialog Provider to facilitate rendering modal on demand.
I have adapted this awesome solution by NearHuscarl from StackOverflow, with some tweaks to meet my needs better. I also have to come up with a solution to resolve stale states in modals created on-demand. Code is shown in below CodeSandbox.
Tweaks
DialogOptions
now extends material-uiDialogProps
so that all Dialog props are available to ourcreateDialog
function. This is powerful because now we customise our modal as if we are working with material-ui'sDialog
component directly. Note that because of this change, ourcloseDialog
is only responsible for removing the last modal, no more worry about modal events likeonClose
.DialogContainer
now takes a title and render a title bar a close icon button.
Problem with asynchronous state
As we know, React state is asynchronous, i.e. we would not get the lastest state right after we set it. We will have it in useEffect
or the next render circle. For example, if we setState('new value')
then console.log(state)
right after, what gets printed in the console will not be "new value" but the previous stale state. If we console log the state in useEffect
, we will have the current state.
This is a problem for our modal because if we use states in our modal, when we set the states and launch our modal right after, our modal would not have the latest state because it is created and rendered in the same render circle. For a demonstration, refresh and click the "Without Hook" button in CodeSandbox. One way to resolve the problem is that we delay and launch the modal in useEffect
.
I have implemented a hook called useRunAsEffect
for the above problem.
It takes a
callback
function.It maintains a call state and initialised to false.
It calls the given callback in a
useEffect
block whenever the call state istrue
. Thecall
is then restored tofalse
right after the call.The hook returns a function. Calling the function would set the
call
flag totrue
and consequently calling whatever is given ascallback
.
I would love to know a better way to do it, and please share if you have one.
Happy coding…
Top comments (0)