javascript - React Router v4 Prompt - 覆盖默认警报

标签 javascript reactjs react-router

React Router v4 <Prompt></Prompt> component非常适合保护导航远离部分填写的表单的用例。

但是如果我们想提供自己的逻辑来代替默认浏览器怎么办 alert()该组件使用? React 旨在创建 UI,因此它似乎是一个非常合理的用例。在 github 中挖掘有关提示的问题我没有发现有人问这个问题。

有人知道为警报提供自定义行为的解决方案吗?

最佳答案

虽然您可以在阻止通过链接在页面之间导航的同时使用自定义模式组件,但是您无法在尝试关闭浏览器或重新加载时显示自定义模式。

但是,如果您不介意,您可以使用 history.listen 来阻止导航。我为它编写了一个通用 HOC 来解决这个用例。

在下面的代码中,列入白名单的路径名是您希望其他人在不显示提示的情况下导航到的路径名

import React from 'react';
import { withRouter } from 'react-router';
import _ from 'lodash';

const navigationPromptFactory = ({ Prompt }) => {
    const initialState = {
        currentLocation: null,
        targetLocation: null,
        isOpen: false
    };

    class NavigationPrompt extends React.Component {
        static defaultProps = {
            when: true
        };

        state = initialState;

        componentDidMount() {
            this.block(this.props);
            window.addEventListener('beforeunload', this.onBeforeUnload);
        }

        componentWillReceiveProps(nextProps) {
            const {
                when: nextWhen,
                history: nextHistory,
                whiteListedPathnames: nextWhiteListedPaths
            } = nextProps;
            const { when, history, whiteListedPathnames } = this.props;
            if (
                when !== nextWhen ||
                !_.isEqual(nextHistory.location, history.location) ||
                !_.isEqual(whiteListedPathnames, nextWhiteListedPaths)
            ) {
                this.unblock();
                this.block(nextProps);
            }
        }

        componentWillUnmount() {
            this.unblock();
            window.removeEventListener('beforeunload', this.onBeforeUnload);
        }

        onBeforeUnload = e => {
            const { when } = this.props;

            // we can't override an onBeforeUnload dialog
            // eslint-disable-next-line
            // https://stackoverflow.com/questions/276660/how-can-i-override-the-onbeforeunload-dialog-and-replace-it-with-my-own

            if (when) {
                // support for custom message is no longer there
                // https://www.chromestatus.com/feature/5349061406228480
                // eslint-disable-next-line
                // https://stackoverflow.com/questions/38879742/is-it-possible-to-display-a-custom-message-in-the-beforeunload-popup

                // setting e.returnValue = "false" to show prompt, reference below
                //https://github.com/electron/electron/issues/2481
                e.returnValue = 'false';
            }
        };

        block = props => {
            const {
                history,
                when,
                whiteListedPathnames = [],
                searchQueryCheck = false
            } = props;
            this.unblock = history.block(targetLocation => {
                const hasPathnameChanged =
                    history.location.pathname !== targetLocation.pathname;
                const hasSearchQueryChanged =
                    history.location.search !== targetLocation.search;
                const hasUrlChanged = searchQueryCheck
                    ? hasPathnameChanged || hasSearchQueryChanged
                    : hasPathnameChanged;
                const isTargetWhiteListed = whiteListedPathnames.includes(
                    targetLocation.pathname
                );
                const hasChanged =
                    when && hasUrlChanged && !isTargetWhiteListed;
                if (hasChanged) {
                    this.setState({
                        currentLocation: history.location,
                        targetLocation,
                        isOpen: true
                    });
                }
                return !hasChanged;
            });
        };

        onConfirm = () => {
            const { history } = this.props;
            const { currentLocation, targetLocation } = this.state;
            this.unblock();
            // replacing current location and then pushing navigates to the target otherwise not
            // this is needed when the user tries to change the url manually
            history.replace(currentLocation);
            history.push(targetLocation);
            this.setState(initialState);
        };

        onCancel = () => {
            const { currentLocation } = this.state;
            this.setState(initialState);
            // Replacing the current location in case the user tried to change the url manually
            this.unblock();
            this.props.history.replace(currentLocation);
            this.block(this.props);
        };

        render() {
            return (
                <Prompt
                    {...this.props}
                    isOpen={this.state.isOpen}
                    onCancel={this.onCancel}
                    onConfirm={this.onConfirm}
                />
            );
        }
    }

    return withRouter(NavigationPrompt);
};

export { navigationPromptFactory };

为了使用上面的内容,您可以简单地提供您的自定义提示模态

      const NavigationPrompt = navigationPromptFactory({
           Prompt: AlertDialog
      });
      const whiteListedPathnames = [`${match.url}/abc`, match.url];

       <NavigationPrompt
                when={isEditingPlan}
                cancelLabel={'Stay'}
                confirmLabel={'Leave'}
                whiteListedPathnames={whiteListedPathnames}
                title={'Leave This Page'}
            >
                <span>
                    Unsaved Changes may not be saved
                </span>
      </NavigationPrompt>

关于javascript - React Router v4 Prompt - 覆盖默认警报,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52437195/

相关文章:

javascript - 如何在react js中更改路由之前进行身份验证?

javascript - 如何为我的函数访问数组中的属性?

javascript - 在 jQuery 中切换 div 的高度

javascript - 指定在任何 Jest 设置发生之前运行的代码

javascript - 如何从javascript中给定的对象日期数组获取当前周

reactjs - 为什么我们在 onClick 事件上使用 Bind 方法

node.js - 处理对React JS应用的发布请求

javascript - NotFoundError 未找到节点 image_div.parentNode.removeChild(img)

reactjs - 双向无限滚动 react native

javascript - React-Router:使用客户端路由的服务器请求过程?