React Router v.6 usePrompt TypeScript

问题描述 投票:0回答:5

我基本上是在尝试拦截路线变化。也许

React Router v6
中类似于 vue 的 beforeEach 的东西可能会很有用,因为 React Router v.6 not 包含
usePrompt

在每次路线更改之前,我想做一些逻辑 - 该逻辑可能需要根据结果中断甚至更改最终路线。

我已经四处寻找,但我真的找不到解决这个特定问题的东西。

提前致谢。

reactjs react-router react-router-dom
5个回答
16
投票

目前他们已经从react-router v6中删除了

usePrompt

我从 ui.dev 找到了一个解决方案,并添加了 TypeScript 支持,现在正在使用它,直到反应路由器将带回

usePrompt
/
useBlocker
钩子

import { History, Transition } from 'history';
import { useCallback, useContext, useEffect } from "react";
import { Navigator } from 'react-router';
import { UNSAFE_NavigationContext as NavigationContext } from "react-router-dom";

type ExtendNavigator = Navigator & Pick<History, "block">;
export function useBlocker(blocker: (tx: Transition) => void, when = true) {
    const { navigator } = useContext(NavigationContext);

    useEffect(() => {
        if (!when) return;

        const unblock = (navigator as ExtendNavigator).block((tx) => {
            const autoUnblockingTx = {
                ...tx,
                retry() {
                    unblock();
                    tx.retry();
                },
            };

            blocker(autoUnblockingTx);
        });

        return unblock;
    }, [navigator, blocker, when]);
}

export default function usePrompt(message: string, when = true) {
    const blocker = useCallback((tx: Transition) => {
        if (window.confirm(message)) tx.retry();
    }, [message]);

    useBlocker(blocker, when);
}

这可以用在任何您希望在条件成立时显示“您确定要离开吗?”消息的视图/组件中。

usePrompt("Do you want to leave?", isFormDirty());

3
投票

针对react-router v6.8.0使用此处列出的其他解决方案,当我尝试将

navigator.block
传递到
true
的第二个参数时,我收到错误,指出
usePrompt

未定义

在对react-router GitHub页面进行了一些挖掘之后,我发现了维护者的文档解释了为什么会发生这些变化,但我也发现了最近推出的这个小gem

虽然库中目前还没有成熟的功能,但

unstable_useBlocker
函数可以完成这项工作。

这是一个对我有用的粗略示例:

import { useEffect, useState } from 'react';
import { unstable_useBlocker } from 'react-router-dom';

const MyComponent = () => {
  const [shouldBlockLeaving, setShouldBlockLeaving] = useState<boolean>(false);

  const blocker = unstable_useBlocker(shouldBlockLeaving);

  if (blocker.state === 'blocked') {
    if (window.confirm('You have unsaved changes, are you sure you want to leave?')) {
      blocker.proceed?.();
    } else {
      blocker.reset?.();
    }
  }

  // reset the blocker if the user cleans the form
  useEffect(() => {
    if (blocker.state === 'blocked' && !shouldBlockLeaving) {
      blocker.reset();
    }
  }, [blocker, shouldBlockLeaving]);

  return (<>
    ...
  </>)
} 

请注意,这是一个不完整的示例,因为我没有将

shouldBlockLeaving
状态设置为
true
,但希望这很容易推断。

这是 Stackblitz 上更彻底的 示例


0
投票

useBlocker
钩子已被重新引入,并且自版本 6.19 起被视为稳定。 官方文档这里.


-1
投票

是的

usePrompt
useBlock
已被删除,但是您可以使用
history.block
实现同样的效果,这里是在 React Router Dom V5 中使用
history.block
和自定义模式来阻止导航的工作示例

import { useHistory } from "react-router-dom";
import { UnregisterCallback } from "history";
...

type Prop = {
  verify?: {
    blockRoute?: (nextRoute: string) => boolean;
  }
};

...
// in the component where you want to show confirmation modal on any nav change

const history = useHistory();
const unblock = useRef<UnregisterCallback>();
const onConfirmExit = () => {
  /**
   * if user confirms to exit, we can allow the navigation
   */

  // Unblock the navigation.
  unblock?.current?.();
  // Proceed with the blocked navigation
  goBack();
};

useEffect(() => {
  /**
   * Block navigation and register a callback that
   * fires when a navigation attempt is blocked.
   */
  unblock.current = history.block(({ pathname: to }) => {
   /**
    * Simply allow the transition to pass immediately,
    * if user does not want to verify the navigate away action,
    * or if user is allowed to navigate to next route without blocking.
    */
   if (!verify || !verify.blockRoute?.(to)) return undefined;

   /**
    * Navigation was blocked! Let's show a confirmation dialog
    * so the user can decide if they actually want to navigate
    * away and discard changes they've made in the current page.
    */
   showConfirmationModal();
   // prevent navigation
   return false;
});

// just in case theres an unmount we can unblock if it exists
  return unblock.current;
}, [history]);


-3
投票

如果你不使用 TS,这里是一个 React-route-dom v6 usePrompt 的 JS 示例。

import { useContext, useEffect, useCallback } from 'react';
import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom';

export function useBlocker( blocker, when = true ) {
    const { navigator } = useContext( NavigationContext );

    useEffect( () => {
        if ( ! when ) return;

        const unblock = navigator.block( ( tx ) => {
            const autoUnblockingTx = {
                ...tx,
                retry() {
                    unblock();
                    tx.retry();
                },
            };

            blocker( autoUnblockingTx );
        } );

        return unblock;
    }, [ navigator, blocker, when ] );
}

export function usePrompt( message, when = true ) {
    const blocker = useCallback(
        ( tx ) => {
            // eslint-disable-next-line no-alert
            if ( window.confirm( message ) ) tx.retry();
        },
        [ message ]
    );

    useBlocker( blocker, when );
}

那么实施将是...

const MyComponent = () => {
    const formIsDirty = true; // Condition to trigger the prompt.
    usePrompt( 'Leave screen?', formIsDirty );
    return (
        <div>Hello world</div> 
    );
};

这是带有示例的文章

© www.soinside.com 2019 - 2024. All rights reserved.