How to intercept back button in a React SPA using function components and React Router v5

怎甘沉沦 提交于 2020-06-01 05:36:06

问题


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.)

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].

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?

(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.


回答1:


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. Install React Router for web, and types - npm install --save react-router-dom @types/react-router-dom.
  2. Import { BrowserRouter, Route, RouteComponentProps, withRouter } from react-router-dom.
  3. Identify the component whose state will change when the back button is pressed.
  4. Pass in history from RouteComponentProps via destructuring:
    function MyComponent( { history }: ReactComponentProps) {
        ...
    }
    
  5. 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.

  6. Handle the changes that should happen when the back-button on the browser is pressed. (Note that component lifecycle methods, like componentDidMount, won't work in a function component. Make sure useEffect is imported from 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. Wrap it in withRouter and create a new component with access to a Route's properties:
    const ComponentWithHistory = withRouter(MyComponent);
    
  8. 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;
    
  9. 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.



来源:https://stackoverflow.com/questions/61939358/how-to-intercept-back-button-in-a-react-spa-using-function-components-and-react

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!