我基本上是在尝试拦截路线变化。也许
React Router v6中类似于 vue 的
beforeEach
的东西可能会很有用,因为 React Router v.6 not 包含 usePrompt
。
在每次路线更改之前,我想做一些逻辑 - 该逻辑可能需要根据结果中断甚至更改最终路线。
我已经四处寻找,但我真的找不到解决这个特定问题的东西。
提前致谢。
目前他们已经从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());
针对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 上更彻底的 示例
是的
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]);
如果你不使用 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>
);
};
这是带有示例的文章