如何使用功能组件和 React Router v5 在 React SPA 中拦截后退按钮

IT技术 reactjs typescript react-router single-page-application
2021-05-23 10:35:04

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

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

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

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

任何和所有的洞察力都表示赞赏。

1个回答

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

  1. 安装 React Router for web,并输入 - npm install --save react-router-dom @types/react-router-dom.
  2. 导入{ BrowserRouter, Route, RouteComponentProps, withRouter }react-router-dom
  3. 标识按下后退按钮时其状态将改变的组件。
  4. 通过在historyRouteComponentProps通过解构:
    function MyComponent( { history }: ReactComponentProps) {
        ...
    }
    
  5. 在屏幕状态更改(用户将其视为新页面的内容)中添加一个空白条目history例如:

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

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

  6. 处理按下浏览器上的后退按钮时应该发生的更改(请注意,组件生命周期方法,例如componentDidMount,在功能组件中不起作用。确保useEffect从 导入react。)
    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.
            }
        }
    })
    
  7. 将其包裹起来withRouter并创建一个可以访问 Route 属性的新组件:
    const ComponentWithHistory = withRouter(MyComponent);
    
  8. 现在将其全部包装在 a<BrowserRouter />和 a 中,<Route />以便 React Router 将其识别为路由器,并将所有路径路由到path="/",除非指定了具有更具体路径的路由(无论如何,使用此设置,所有路径都相同,由于history.push("");历史看起来像["", "", "", "", ""])。

    function App() {
    
    return (
        <BrowserRouter>
            <Route path="/">
                <ThemeProvider theme={theme}>
                    <ComponentWithHistory />
                </ThemeProvider>
            </Route>
        </BrowserRouter>
        );
    }
    
    export default App;
    
  9. 一个完整的例子现在看起来像这样:

    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;
    

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