自定义 React Router 提示:历史记录一次仅支持一个提示

Kri*_*ado 7 reactjs react-router material-ui

我创建了一个自定义的 React Router Prompt 来显示用户何时想要导航到其他页面并且尚未保存数据。

一切似乎都按预期进行,但我有一个恼人的警告:

Warning: A history supports only one prompt at a time
Run Code Online (Sandbox Code Playgroud)

该组件如下所示:

export function RouterPrompt(props) {
    const {when, onOK, onCancel, title} = props;

    const history = useHistory();

    const [showPrompt, setShowPrompt] = useState(false);
    const [currentPath, setCurrentPath] = useState("");

    useEffect(() => {
        if (when) {
            history.block((prompt) => {
                setCurrentPath(prompt.pathname);
                setShowPrompt(true);
                return "true";
            });
        } else {
            history.block(() => {
            });
        }
    }, [when]);

    const handleOK = useCallback(async () => {
        if (onOK) {
            const canRoute = await Promise.resolve(onOK());
            if (canRoute) {
                history.block(() => {
                });
                history.push(currentPath);
            }
        }
    }, [currentPath, history, onOK]);

    const handleCancel = useCallback(async () => {
        if (onCancel) {
            const canRoute = await Promise.resolve(onCancel());
            if (canRoute) {
                history.block(() => {
                });
                history.push(currentPath);
            }
        }
        setShowPrompt(false);
    }, [currentPath, history, onCancel]);

    return showPrompt ? (
        <Dialog
            open={showPrompt}
            onClose={handleCancel}
            aria-labelledby="alert-dialog-title"
            aria-describedby="alert-dialog-description"
        >
            <DialogTitle id="alert-dialog-title">
                {title}
            </DialogTitle>
            <DialogContent>
                <DialogContentText id="alert-dialog-description">
                    There are unsaved changes. Are you sure want to leave this page?
                </DialogContentText>
            </DialogContent>
            <DialogActions>
                <Button onClick={handleCancel}>Stay on page</Button>
                <Button onClick={handleOK} autoFocus>
                    Leave
                </Button>
            </DialogActions>
        </Dialog>
    ) : null;
}
Run Code Online (Sandbox Code Playgroud)

据我对此进行的一些研究,这来自类生命周期方法(useEffect在取消挂载当前history.block之前运行),但实际上在DOM中只渲染了一个Prompt。

编辑

我发现了另一个错误,无论是否实际推送,这都会改变路径。因此,在 URL 中,路径会发生变化,但视图不会发生变化,因此下次当您按下“后退”和“确认”时,而不是停留在页面上,您将在历史记录中后退 2 步。

Kri*_*ado 11

我已经完全重组/重构了我的代码(原始代码来自博客文章),这似乎工作完美(没有警告)并且符合预期:

export function RouterPrompt(props) {
    const {when, title} = props;

    const history = useHistory();

    const [showPrompt, setShowPrompt] = useState(false);
    const [currentPath, setCurrentPath] = useState("");

    const unblockRef = useRef();

    function handleShowModal() {
        setShowPrompt(true);
    }

    function onCancel() {
        setShowPrompt(false);
    }

    useEffect(() => {
        unblockRef.current = history.block((location) => {
            if (when) {
                setCurrentPath(location.pathname);
                handleShowModal();
                return false;
            }
            return true;
        });
        return () => {
            unblockRef.current && unblockRef.current();
        };
    }, [when]);

    function handleConfirm() {
        if (unblockRef) {
            unblockRef.current();
        }
        setShowPrompt(false);
        history.push(currentPath);
    }

    return (
        <Dialog
            open={showPrompt}
            onClose={onCancel}
            aria-labelledby="alert-dialog-title"
            aria-describedby="alert-dialog-description"
        >
            <DialogTitle id="alert-dialog-title">
                {title}
            </DialogTitle>
            <DialogContent>
                <DialogContentText id="alert-dialog-description">
                    There are unsaved changes. Are you sure want to leave this page?
                </DialogContentText>
            </DialogContent>
            <DialogActions>
                <Button onClick={onCancel}>Stay on page</Button>
                <Button onClick={handleConfirm} autoFocus>
                    Leave
                </Button>
            </DialogActions>
        </Dialog>
    );
}
Run Code Online (Sandbox Code Playgroud)