Closed ghost closed 6 years ago
It's outside of the scope of the library. You need to wire the open state with the URL.
@oliviertassinari ok thanks.
Will search for an example of how to do that online.
I almost thought about this issue at the same time as you @gedw99! ^^ If you find something nice, could you please share it here ? :)
Any solution for this?
I'm a bit confused because the mUI docs for the Dialog example state that:
Touching outside of the dialog, or pressing Back, cancels the action and closes the dialog
But this does not appear to be the case?
@JulesAU Indeed I think this sentence is not correct.
I've made a demo that binds the open state with the location hash, like this, clicking the back button works as expected: https://codesandbox.io/s/material-demo-7zf07 (the back button does not seem to work as expected in Codesandbox preview, but if you open the result on a dedicated page, it works as expected: https://7zf07.codesandbox.io)
@oliviertassinari do you think this could be added to the docs?
@ValentinH Thanks for sharing your demo. I believe this issue is enough as documentation.
I made a React hook useUrlHashState()
based on @ValentinH 's answer.
import { SetStateAction, useEffect, useState } from 'react';
/**
* use a boolean state that binds to a url hash
* @param hash a string starts with '#'
* @returns current state, and a function to update it
*/
export function useUrlHashState(
hash: string
): [state: boolean, setState: React.Dispatch<SetStateAction<boolean>>] {
const [state, setState] = useState<boolean>(window.location.hash === hash);
useEffect(() => {
const onHashChange = () => setState(window.location.hash === hash);
window.addEventListener('hashchange', onHashChange);
return () => window.removeEventListener('hashchange', onHashChange);
}, [hash]);
return [
state,
setStateAction => {
if (
(typeof setStateAction === 'boolean' && setStateAction) ||
(typeof setStateAction === 'function' && setStateAction(state))
) {
window.location.hash = hash;
} else {
window.history.back();
}
},
];
}
A problem with ValentinH solution occurs when spamming clicks really fast. It triggers a bunch of back navigations, potentially skipping many pages of history.
You can use MUI useTheme
and useMediaQuery
hooks to track screen size:
Make your own wrapper component with additional useEffect and callback to handle the history change e.g.:
import React, { useCallback, useEffect } from 'react';
import ButtonBase from '@material-ui/core/ButtonBase';
import Dialog from '@material-ui/core/Dialog';
type Props = {
open: boolean;
onClose: () => void;
};
export const MyDialog: React.FC<Props> = ({ open, onClose, children }) => {
useEffect(() => {
if (!open) return;
window.history.pushState('forward', '', '#dialog');
window.addEventListener('popstate', onClose);
return () => {
window.removeEventListener('popstate', onClose);
};
}, [open, onClose]);
const onCloseModal = useCallback(() => {
onClose();
window.history.pushState(
'forward',
'',
window.location.pathname + window.location.search
);
}, [onClose]);
return (
<Dialog open={open} onClose={onCloseModal}>
<ButtonBase onClick={onCloseModal}>X</ButtonBase>
{children}
</Dialog>
);
};
Make your own wrapper component with additional useEffect and callback to handle the history change e.g.:
import React, { useCallback, useEffect } from 'react'; import ButtonBase from '@material-ui/core/ButtonBase'; import Dialog from '@material-ui/core/Dialog'; type Props = { open: boolean; onClose: () => void; }; export const MyDialog: React.FC<Props> = ({ open, onClose, children }) => { useEffect(() => { if (!open) return; window.history.pushState('forward', '', '#dialog'); window.addEventListener('popstate', onClose); return () => { window.removeEventListener('popstate', onClose); }; }, [open, onClose]); const onCloseModal = useCallback(() => { onClose(); window.history.pushState( 'forward', '', window.location.pathname + window.location.search ); }, [onClose]); return ( <Dialog open={open} onClose={onCloseModal}> <ButtonBase onClick={onCloseModal}>X</ButtonBase> {children} </Dialog> ); };
This one is something, but the history is being saved actually. So if you open and close the dialog several times, then you will go through all this history when clicking browser back button. Also, I think you need to use onCloseModal
function for popstate
listener, not onClose
.
onClose();
what does onClose() function do here? Can someone post a complete example please
Having a full screen dialog really is a huge improvement for mobile. But it really really needs to support the Back button. It's so counter intuitive for users when they hit the back button on their mobile and navigate them the actual page they were on.
Is it hard to add this because of the different routers used ?