programing

리액트 라우터에서 브라우저의 뒤로 버튼을 가로채거나 처리합니까?

topblog 2023. 3. 14. 21:22
반응형

리액트 라우터에서 브라우저의 뒤로 버튼을 가로채거나 처리합니까?

제어되는 Material-ui 탭을 사용하고 있으며 다음과 같은 (React-Router) 링크에 사용합니다.

    <Tab value={0} label="dashboard" containerElement={<Link to="/dashboard/home"/>}/>
    <Tab value={1} label="users" containerElement={<Link to="/dashboard/users"/>} />
  <Tab value={2} label="data" containerElement={<Link to="/dashboard/data"/>} />

대시보드/데이터를 등록하고 브라우저의 뒤로 버튼을 클릭하면 대시보드/사용자에게 이동하지만 강조 표시된 탭은 대시보드/데이터(값=2)에 그대로 남습니다.

상태를 설정해서 변경할 수 있는데 브라우저의 뒤로 버튼을 눌렀을 때 이벤트를 어떻게 처리해야 할지 모르겠어요.

이걸 찾았어요

window.onpopstate = this.onBackButtonEvent;

단, 이것은 상태가 변경될 때마다(뒤로 버튼 이벤트뿐만 아니라) 호출됩니다.

리액트 라우터를 사용하면 다음과 같이 작업이 간단해집니다.

import { browserHistory } from 'react-router';

componentDidMount() {
    this.onScrollNearBottom(this.scrollToLoad);

    this.backListener = browserHistory.listen((loc, action) => {
      if (action === "POP") {
        // Do your stuff
      }
    });
  }

componentWillUnmount() {
    // Unbind listener
    this.backListener();
}

후크를 사용하여 뒤로 및 앞으로 버튼을 감지할 수 있습니다.

import { useHistory } from 'react-router-dom'


const [ locationKeys, setLocationKeys ] = useState([])
const history = useHistory()

useEffect(() => {
  return history.listen(location => {
    if (history.action === 'PUSH') {
      setLocationKeys([ location.key ])
    }

    if (history.action === 'POP') {
      if (locationKeys[1] === location.key) {
        setLocationKeys(([ _, ...keys ]) => keys)

        // Handle forward event

      } else {
        setLocationKeys((keys) => [ location.key, ...keys ])

        // Handle back event

      }
    }
  })
}, [ locationKeys, ])

결국 이렇게 된 거죠.

componentDidMount() {
    this._isMounted = true;
    window.onpopstate = ()=> {
      if(this._isMounted) {
        const { hash } = location;
        if(hash.indexOf('home')>-1 && this.state.value!==0)
          this.setState({value: 0})
        if(hash.indexOf('users')>-1 && this.state.value!==1)
          this.setState({value: 1})
        if(hash.indexOf('data')>-1 && this.state.value!==2)
          this.setState({value: 2})
      }
    }
  }

모두 도와줘서 고마워 lol

훅 샘플

const {history} = useRouter();
  useEffect(() => {
    return () => {
      // && history.location.pathname === "any specific path")
      if (history.action === "POP") {
        history.replace(history.location.pathname, /* the new state */);
      }
    };
  }, [history])

나는 역사를 사용하지 않는다. 왜냐하면 그것은 주에 영향을 주지 않기 때문이다.

const disposeListener = history.listen(navData => {
        if (navData.pathname === "/props") {
            navData.state = /* the new state */;
        }
    });

이 질문에 대한 대부분의 답변은 오래된 버전의 React Router를 사용하거나 최신 클래스 컴포넌트에 의존하거나 혼란스러운 경우입니다.또한 일반적인 조합인 타입 스크립트를 사용하는 경우는 없습니다.다음으로 Router v5, 함수 컴포넌트 및 Typescript를 사용한 답변을 나타냅니다.

// use destructuring to access the history property of the ReactComponentProps type
function MyComponent( { history }: ReactComponentProps) {

    // use useEffect to access lifecycle methods, as componentDidMount etc. are not available on function components.
    useEffect(() => {

        return () => {
            if (history.action === "POP") {
                // Code here will run when back button fires. Note that it's after the `return` for useEffect's callback; code before the return will fire after the page mounts, code after when it is about to unmount.
                }
           }
    })
}

자세한 설명은 이쪽에서 보실 수 있습니다.

React Router API 버전 3.x에는 이벤트가 브라우저 이력에 등록되기 전에 "뒤로" 버튼 이벤트를 표시하기 위해 사용할 수 있는 유틸리티 세트가 있습니다.먼저 컴포넌트를 고차 컴포넌트로 랩해야 합니다.그 후 를 사용할 수 있습니다.setRouteLeaveHook()의 ""를 받아들이는 함수route이 있는 물건path속성 및 콜백 함수.

import {Component} from 'react';
import {withRouter} from 'react-router';

class Foo extends Component {
  componentDidMount() {
    this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave);
  }

  routerWillLeave(nextState) { // return false to block navigation, true to allow
    if (nextState.action === 'POP') {
      // handle "Back" button clicks here
    }
  }
}

export default withRouter(Foo);

후크 사용.@Nicolas Keller의 코드를 타이프 스크립트로 변환했습니다.

  const [locationKeys, setLocationKeys] = useState<(string | undefined)[]>([]);
  const history = useHistory();

  useEffect(() => {
    return history.listen((location) => {
      if (history.action === 'PUSH') {
        if (location.key) setLocationKeys([location.key]);
      }

      if (history.action === 'POP') {
        if (locationKeys[1] === location.key) {
          setLocationKeys(([_, ...keys]) => keys);

          // Handle forward event
          console.log('forward button');
        } else {
          setLocationKeys((keys) => [location.key, ...keys]);

          // Handle back event
          console.log('back button');
          removeTask();
        }
      }
    });
  }, [locationKeys]);

이력 프로펠을 가져와 componentDidMount() 메서드를 쓰기 위해 라우터 hoc과 함께 사용했습니다.

componentDidMount() {
    if (this.props.history.action === "POP") {
        // custom back button implementation
    }
}

NextJs에서는 peforePopState 함수를 사용하여 가까운 모달 또는 모달 또는 백주소를 확인하고 무엇을 할지 결정할 수 있습니다.

const router = useRouter();

useEffect(() => {
    router.beforePopState(({ url, as, options }) => {
        // I only want to allow these two routes!

        if (as === '/' ) {
            // Have SSR render bad routes as a 404.
             window.location.href = as;
            closeModal();
            return false
        }

        return true
    })
}, [])

반응 기능 컴포넌트에서 브라우저를 눌렀을 때 경고를 표시하려면 다음 단계를 수행합니다.

  1. isBackButton클릭 후 false로 초기화하고 setBackbuttonPress 기능을 사용하여 상태를 유지합니다.
const [isBackButtonClicked, setBackbuttonPress] = useState(false);
  1. componentdidmount에 다음 코드 행을 추가합니다.
window.history.pushState(null, null, window.location.pathname);
window.addEventListener('popstate', onBackButtonEvent);
  1. 필요에 따라 onBackButtonEvent 함수를 정의하고 로직을 작성합니다.

      const onBackButtonEvent = (e) => {
      e.preventDefault();
      if (!isBackButtonClicked) {
    
      if (window.confirm("Do you want to go to Test Listing")) {
        setBackbuttonPress(true)
        props.history.go(listingpage)
      } else {
        window.history.pushState(null, null, window.location.pathname);
        setBackbuttonPress(false)
      }
    }
    

    }

  2. in component will mount on BackButtonEvent 함수 구독 취소

최종 코드는 다음과 같습니다.

import React,{useEffect,useState} from 'react'

function HandleBrowserBackButton() {
  const [isBackButtonClicked, setBackbuttonPress] = useState(false)

  useEffect(() => {

    window.history.pushState(null, null, window.location.pathname);
    window.addEventListener('popstate', onBackButtonEvent);

    //logic for showing popup warning on page refresh
    window.onbeforeunload = function () {

      return "Data will be lost if you leave the page, are you sure?";
    };
    return () => {
      window.removeEventListener('popstate', onBackButtonEvent);
    }

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
  const onBackButtonEvent = (e) => {
    e.preventDefault();
    if (!isBackButtonClicked) {

      if (window.confirm("Do you want to go to Test Listing")) {
        setBackbuttonPress(true)
        props.history.go(listingpage)
      } else {
        window.history.pushState(null, null, window.location.pathname);
        setBackbuttonPress(false)
      }
    }
  }

  return (
    <div>

    </div>
  )
}

export default HandleBrowserBackButton

React Router V5 를 사용하고 있는 경우는, Prompt 를 사용해 주세요.

페이지에서 이동하기 전에 사용자에게 묻기 위해 사용됩니다.어플리케이션이 사용자가 이동할 수 없는 상태가 되면(폼이 반쯤 채워진 것처럼) <프롬프트>를 렌더링합니다.

<Prompt
   message={(location, action) => {
   if (action === 'POP') {
      console.log("Backing up...")
      // Add your back logic here
   }

   return true;
   }}
/>

componentDidMount()를 입력합니다.

componentDidMount() {
    window.onbeforeunload =this.beforeUnloadListener;
} 
beforeUnloadListener = (event) => {
    event.preventDefault();
    return event.returnValue = "Are you sure you want to exit?";
};

componentDidMount()에 이들 2줄을 추가합니다.이건 내게 효과가 있었다.

window.history.pushState(null, null, document.URL);
window.addEventListener('popstate', function(event) {
      window.location.replace(
        `YOUR URL`
      );
});

이는 React에서 사용하는 라우터의 유형에 따라 달라집니다.

위와 같이 리액트 라우터에서 BrowserRouter를 사용하는 경우(단, 리액트 라우터 v4에서는 사용할 수 없음), 'POP' 액션을 사용하여 브라우저의 뒤로 버튼을 가로챌 수 있습니다.

단, HashRouter를 사용하여 루트를 푸시하는 경우 위의 솔루션은 작동하지 않습니다.그 이유는 해시 라우터가 브라우저의 뒤로 버튼을 클릭하거나 컴포넌트에서 경로를 푸시할 때 항상 'POP' 액션으로 트리거되기 때문입니다.이 두 가지 작업은 window.popstate 또는 history.listen을 사용하여 구분할 수 없습니다.

향후 버전 6.0에서는 모든 탐색 시도를 가로채는 데 사용할 수 있는 useBlocker 훅이 도입되었습니다.

import { Action } from 'history';
import { useBlocker } from 'react-router';

// when blocker should be active
const unsavedChanges = true;

useBlocker((transition) => {
    const {
        location, // The new location
        action, // The action that triggered the change
    } = transition;

    // intercept back and forward actions:
    if (action === Action.Pop) {
        alert('intercepted!')
    }

}, unsavedChanges);

'라우터' HOC을 사용하여this.props.history.goBack.

<Button onClick={this.props.history.goBack}>
    BACK
</Button>

언급URL : https://stackoverflow.com/questions/39342195/intercept-handle-browsers-back-button-in-react-router

반응형