如何使用功能组件和 React Router v5 在 React SPA 中拦截后退按钮 [英] How to intercept back button in a React SPA using function components and React Router v5

查看:49
本文介绍了如何使用功能组件和 React Router v5 在 React SPA 中拦截后退按钮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 React 中的 SPA 中工作,该 SPA 不使用 React Router 创建任何路由;我不需要允许用户导航到特定页面.(想想多页问卷,按顺序填写.)

I'm working in a SPA in React that doesn't use React Router to create any Routes; I don't need to allow users to navigate to specific pages. (Think multi-page questionnaire, to be filled in sequentially.)

但是,当用户按浏览器上的后退按钮时,我不希望他们退出整个应用程序;我希望能够在用户按下后退按钮时触发一个功能,该按钮只是呈现页面,就像他们上次选择之前一样.(每个页面都是一个组件,它们组装在一个数组中,由 currentPage 状态变量(来自状态挂钩)跟踪,因此我可以简单地呈现 pages[currentPage -1].

But, when users press the back button on the browser, I don't want them to exit the whole app; I want to be able to fire a function when the user presses the back button that simply renders the page as it was before their last selection. (Each page is a component, and they're assembled in an array, tracked by a currentPage state variable (from a state hook), so I can simply render the pages[currentPage -1].

使用(如有必要)当前版本的 React Router (v5)、函数组件和 Typescript,我如何访问后退按钮事件以禁用它并将其替换为我自己的函数?

Using (if necessary) the current version of React Router (v5), function components, and Typescript, how can I access the back-button event to both disable it and replace it with my own function?

(我发现的其他答案要么使用类组件、特定于旧版 React Router 的函数,要么使用特定框架,例如 Next.js.)

(Other answers I've found either use class components, functions specific to old versions of React Router, or specific frameworks, like Next.js.)

任何和所有见解都值得赞赏.

Any and all insight is appreciated.

推荐答案

经过太多小时的工作,我找到了解决方案.因为一旦我找到了正确的路径,它最终并不是那么困难,所以我发布了我的解决方案,希望它可以节省其他人的时间.

After way too many hours of work, I found a solution. As it ultimately was not that difficult once I found the proper path, I'm posting my solution in the hope it may save others time.

  1. 为 web 和类型安装 React Router - npm install --save react-router-dom @types/react-router-dom.
  2. react-router-dom导入{ BrowserRouter, Route, RouteComponentProps, withRouter }.
  3. 标识当按下后退按钮时其状态会改变的组件.
  4. 通过解构从 RouteComponentProps 传入 history:
function MyComponent( { history }: ReactComponentProps) {
    ...
}

  • 屏幕状态变化(用户将其视为新页面)向history添加一个空白条目;例如:

  • On screen state change (what the user would perceive as a new page) add a blank entry to history; for example:

    function MyComponent( { history }: ReactComponentProps) {
    
        const handleClick() {
            history.push('')
        }
    
    }
    

    这会在历史记录中创建一个空白条目,因此历史记录具有后退按钮可以访问的条目;但用户看到的网址不会改变.

    This creates a blank entry in history, so history has entries that the back button can access; but the url the user sees won't change.

    useEffect(() => {
        // code here would fire when the page loads, equivalent to `componentDidMount`.
        return () => {
            // code after the return is equivalent to `componentWillUnmount`
            if (history.action === "POP") {
                // handle any state changes necessary to set the screen display back one page.
            }
        }
    })
    

  • 将其包裹在 withRouter 中并创建一个可以访问 Route 属性的新组件:

  • Wrap it in withRouter and create a new component with access to a Route's properties:

    const ComponentWithHistory = withRouter(MyComponent);
    

  • 现在将其全部包装在 中,以便 React Router 将其识别为路由器,并将所有路径路由到 path="/",它将捕获所有路由,除非指定了具有更具体路径的路由(无论如何,由于 history,使用此设置,所有路径都是相同的.push(""); 历史记录看起来像 ["", "", "", "", ""]).

  • Now wrap it all in a <BrowserRouter /> and a <Route /> so that React Router recognizes it as a router, and route all paths to path="/", which will catch all routes unless Routes with more specific paths are specified (which will all be the same anyway, with this setup, due to history.push(""); the history will look like ["", "", "", "", ""]).

    function App() {
    
    return (
        <BrowserRouter>
            <Route path="/">
                <ThemeProvider theme={theme}>
                    <ComponentWithHistory />
                </ThemeProvider>
            </Route>
        </BrowserRouter>
        );
    }
    
    export default App;
    

  • 一个完整的例子现在看起来像这样:

  • A full example now looks something like this:

    function MyComponent( { history }: ReactComponentProps) {
        // use a state hook to manage a "page" state variable
        const [page, setPage] = React.useState(0)
    
    
        const handleClick() {
            setPage(page + 1);
            history.push('');
        }
    
        useEffect(() => {
        return () => {
            if (history.action === "POP") {
                // set state back one to render previous "page" (state)
                setPage(page - 1)
            }
        }
     })
    }
    
    const ComponentWithHistory = withRouter(MyComponent);
    
    function App() {
    
        return (
            <BrowserRouter>
                <Route path="/">
                    <ThemeProvider theme={theme}>
                        <ComponentWithHistory />
                    </ThemeProvider>
                </Route>
            </BrowserRouter>
            );
        }
    
    export default App;
    

  • 如果有更好的方法,我很乐意听到;但这对我来说效果很好.

    If there are better ways, I would love to hear; but this is working very well for me.

    这篇关于如何使用功能组件和 React Router v5 在 React SPA 中拦截后退按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆