[reactjs] react-router로 페이지를 떠나는 사용자 감지
특정 페이지에서 이동할 때 ReactJS 앱이 사용자에게 알리기를 원합니다. 특히 조치를 취하도록 상기시키는 팝업 메시지 :
“변경 사항이 저장되었지만 아직 게시되지 않았습니다. 지금 수행 하시겠습니까?”
이를 react-router
전역 적으로 트리거해야합니까 , 아니면 반응 페이지 / 구성 요소 내에서 수행 할 수 있습니까?
나는 후자에서 아무것도 찾지 못했고 차라리 첫 번째를 피하고 싶습니다. 물론 표준이 아니라면 사용자가 이동할 수있는 다른 모든 페이지에 코드를 추가하지 않고도 이러한 작업을 수행하는 방법이 궁금합니다.
모든 통찰력을 환영합니다, 감사합니다!
답변
react-router
v4는를 사용하여 탐색을 차단하는 새로운 방법을 도입했습니다 Prompt
. 차단하려는 구성 요소에 다음을 추가하십시오.
import { Prompt } from 'react-router'
const MyComponent = () => (
<React.Fragment>
<Prompt
when={shouldBlockNavigation}
message='You have unsaved changes, are you sure you want to leave?'
/>
{/* Component JSX */}
</React.Fragment>
)
이렇게하면 라우팅이 차단되지만 페이지 새로 고침이나 닫기는 차단되지 않습니다. 이를 차단하려면 다음을 추가해야합니다 (필요에 따라 적절한 React 수명 주기로 업데이트).
componentDidUpdate = () => {
if (shouldBlockNavigation) {
window.onbeforeunload = () => true
} else {
window.onbeforeunload = undefined
}
}
onbeforeunload 는 브라우저에서 다양한 지원을 제공합니다.
답변
react-router v2.4.0
이상에서 v4
몇 가지 옵션이 있기 전에
<Route
path="/home"
onEnter={ auth }
onLeave={ showConfirm }
component={ Home }
>
전환이 발생하지 않도록하거나 나가기 후크를 사용하여 경로를 떠나기 전에 사용자에게 메시지를 표시 할 수 있습니다.
const Home = withRouter(
React.createClass({
componentDidMount() {
this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave)
},
routerWillLeave(nextLocation) {
// return false to prevent a transition w/o prompting the user,
// or return a string to allow the user to decide:
// return `null` or nothing to let other hooks to be executed
//
// NOTE: if you return true, other hooks will not be executed!
if (!this.state.isSaved)
return 'Your work is not saved! Are you sure you want to leave?'
},
// ...
})
)
이 예제에서는에 withRouter
도입 된 고차 구성 요소를 사용합니다.v2.4.0.
그러나 이러한 솔루션은 URL에서 경로를 수동으로 변경할 때 완벽하게 작동하지 않습니다.
의미에서
- 확인이 표시됩니다.
- 페이지 포함이 다시로드되지 않음-확인
- URL이 변경되지 않음-괜찮지 않음
들어 react-router v4
프롬프트 또는 사용자 정의 역사를 사용하여 :
그러나에서는 from’react-router react-router v4
의 도움으로 구현하기가 다소 쉽습니다.Prompt
문서에 따르면
신속한
페이지에서 이동하기 전에 사용자에게 메시지를 표시하는 데 사용됩니다. 애플리케이션이 사용자가 다른 곳으로 이동하지 못하도록하는 상태가되면 (예 : 양식이 반만 채워져 있음)
<Prompt>
.import { Prompt } from 'react-router' <Prompt when={formIsHalfFilledOut} message="Are you sure you want to leave?" />
메시지 : 문자열
사용자가 다른 곳으로 이동하려고 할 때 묻는 메시지입니다.
<Prompt message="Are you sure you want to leave?"/>
메시지 : func
사용자가 탐색을 시도하는 다음 위치 및 작업과 함께 호출됩니다. 사용자에게 프롬프트를 표시하려면 문자열을 반환하고 전환을 허용하려면 true를 반환합니다.
<Prompt message={location => ( `Are you sure you want to go to ${location.pathname}?` )}/>
언제 : bool
<Prompt>
가드 뒤를 조건부로 렌더링하는 대신 항상 렌더링 할 수 있지만 그에 따라 통과when={true}
하거나when={false}
탐색을 방지하거나 허용 할 수 있습니다.
렌더링 방법에서 필요에 따라 문서에 언급 된대로이를 추가하기 만하면됩니다.
최신 정보:
페이지를 사용할 때 사용자 지정 작업을 수행하려면 사용자 지정 기록을 사용하고 다음과 같이 라우터를 구성 할 수 있습니다.
history.js
import createBrowserHistory from 'history/createBrowserHistory'
export const history = createBrowserHistory()
...
import { history } from 'path/to/history';
<Router history={history}>
<App/>
</Router>
그런 다음 구성 요소에서 다음 history.block
과 같이 사용할 수 있습니다.
import { history } from 'path/to/history';
class MyComponent extends React.Component {
componentDidMount() {
this.unblock = history.block(targetLocation => {
// take your action here
return false;
});
}
componentWillUnmount() {
this.unblock();
}
render() {
//component render here
}
}
답변
대한 react-router
2.4.0+
참고 : react-router
새로운 기능을 모두 얻으려면 모든 코드를 최신 코드로 마이그레이션하는 것이 좋습니다 .
react-router 문서 에서 권장하는대로 :
withRouter
고차 구성 요소를 사용해야합니다 .
우리는이 새로운 HoC가 더 좋고 더 쉬우 며 문서와 예제에서 사용할 것이라고 생각하지만 전환하는 것이 어려운 요구 사항은 아닙니다.
문서의 ES6 예 :
import React from 'react'
import { withRouter } from 'react-router'
const Page = React.createClass({
componentDidMount() {
this.props.router.setRouteLeaveHook(this.props.route, () => {
if (this.state.unsaved)
return 'You have unsaved information, are you sure you want to leave this page?'
})
}
render() {
return <div>Stuff</div>
}
})
export default withRouter(Page)
답변
대한 react-router
V3.0의
페이지에 저장되지 않은 변경 사항에 대한 확인 메시지가 필요한 동일한 문제가 발생했습니다. 제 경우에는 React Router v3을 사용 하고 있었기 때문에 React Router v4<Prompt />
에서 도입 한을 사용할 수 없었습니다 .
나는의 조합으로 ‘뒤로’버튼 클릭 ‘과’실수로 링크를 클릭 ‘으로 처리 setRouteLeaveHook
하고 history.pushState()
, 그리고 함께’새로 고침 버튼을 ‘처리하는 onbeforeunload
이벤트 핸들러.
setRouteLeaveHook ( doc ) 및 history.pushState ( doc )
-
setRouteLeaveHook 만 사용하는 것만으로는 충분하지 않았습니다. 왠지 ‘뒤로’버튼을 클릭했을 때 페이지는 그대로 유지 되었으나 URL이 변경되었습니다.
// setRouteLeaveHook returns the unregister method this.unregisterRouteHook = this.props.router.setRouteLeaveHook( this.props.route, this.routerWillLeave ); ... routerWillLeave = nextLocation => { // Using native 'confirm' method to show confirmation message const result = confirm('Unsaved work will be lost'); if (result) { // navigation confirmed return true; } else { // navigation canceled, pushing the previous path window.history.pushState(null, null, this.props.route.path); return false; } };
onbeforeunload ( 문서 )
-
‘실수 재 장전’버튼 처리에 사용됩니다.
window.onbeforeunload = this.handleOnBeforeUnload; ... handleOnBeforeUnload = e => { const message = 'Are you sure?'; e.returnValue = message; return message; }
아래는 내가 작성한 전체 구성 요소입니다.
- 참고 withRouter가 가지고하는 데 사용됩니다
this.props.router
. - 참고
this.props.route
호출 구성 요소에서 아래로 전달됩니다 -
참고
currentState
소품으로 전달되어 초기 상태가하고 변화를 확인하기 위해import React from 'react'; import PropTypes from 'prop-types'; import _ from 'lodash'; import { withRouter } from 'react-router'; import Component from '../Component'; import styles from './PreventRouteChange.css'; class PreventRouteChange extends Component { constructor(props) { super(props); this.state = { // initialize the initial state to check any change initialState: _.cloneDeep(props.currentState), hookMounted: false }; } componentDidUpdate() { // I used the library called 'lodash' // but you can use your own way to check any unsaved changed const unsaved = !_.isEqual( this.state.initialState, this.props.currentState ); if (!unsaved && this.state.hookMounted) { // unregister hooks this.setState({ hookMounted: false }); this.unregisterRouteHook(); window.onbeforeunload = null; } else if (unsaved && !this.state.hookMounted) { // register hooks this.setState({ hookMounted: true }); this.unregisterRouteHook = this.props.router.setRouteLeaveHook( this.props.route, this.routerWillLeave ); window.onbeforeunload = this.handleOnBeforeUnload; } } componentWillUnmount() { // unregister onbeforeunload event handler window.onbeforeunload = null; } handleOnBeforeUnload = e => { const message = 'Are you sure?'; e.returnValue = message; return message; }; routerWillLeave = nextLocation => { const result = confirm('Unsaved work will be lost'); if (result) { return true; } else { window.history.pushState(null, null, this.props.route.path); if (this.formStartEle) { this.moveTo.move(this.formStartEle); } return false; } }; render() { return ( <div> {this.props.children} </div> ); } } PreventRouteChange.propTypes = propTypes; export default withRouter(PreventRouteChange);
질문이 있으면 알려주세요 🙂
답변
들어 react-router
와 v0.13.x react
v0.13.x :
이는 willTransitionTo()
및 willTransitionFrom()
정적 메서드를 사용하여 가능 합니다. 최신 버전은 아래의 다른 답변을 참조하십시오.
로부터 반응-라우터 설명서 :
경로 전환 중에 호출 될 경로 처리기에서 일부 정적 메서드를 정의 할 수 있습니다.
willTransitionTo(transition, params, query, callback)
핸들러가 렌더링 되려고 할 때 호출되어 전환을 중단하거나 리디렉션 할 수 있습니다. 비동기 작업을 수행하는 동안 전환을 일시 중지하고 완료되면 callback (error)을 호출하거나 인수 목록에서 콜백을 생략하면 호출됩니다.
willTransitionFrom(transition, component, callback)
활성 경로가 전환 될 때 호출되어 전환을 중단 할 수 있습니다. 구성 요소는 현재 구성 요소이므로 전환을 허용할지 여부를 결정하기 위해 상태를 확인하는 데 필요할 수 있습니다 (양식 필드 등).
예
var Settings = React.createClass({ statics: { willTransitionTo: function (transition, params, query, callback) { auth.isLoggedIn((isLoggedIn) => { transition.abort(); callback(); }); }, willTransitionFrom: function (transition, component) { if (component.formHasUnsavedData()) { if (!confirm('You have unsaved information,'+ 'are you sure you want to leave this page?')) { transition.abort(); } } } } //... });
들어 react-router
1.0.0-RC1 react
이후 v0.14.x 또는 :
이것은 routerWillLeave
라이프 사이클 후크 로 가능해야합니다 . 이전 버전의 경우 위의 내 대답을 참조하십시오.
로부터 반응-라우터 설명서 :
이 후크를 설치하려면 경로 구성 요소 중 하나에서 수명주기 믹스 인을 사용합니다.
import { Lifecycle } from 'react-router' const Home = React.createClass({ // Assuming Home is a route component, it may use the // Lifecycle mixin to get a routerWillLeave method. mixins: [ Lifecycle ], routerWillLeave(nextLocation) { if (!this.state.isSaved) return 'Your work is not saved! Are you sure you want to leave?' }, // ... })
소지품. 최종 출시 전에 변경 될 수 있습니다.
답변
이 프롬프트를 사용할 수 있습니다.
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link, Prompt } from "react-router-dom";
function PreventingTransitionsExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Form</Link>
</li>
<li>
<Link to="/one">One</Link>
</li>
<li>
<Link to="/two">Two</Link>
</li>
</ul>
<Route path="/" exact component={Form} />
<Route path="/one" render={() => <h3>One</h3>} />
<Route path="/two" render={() => <h3>Two</h3>} />
</div>
</Router>
);
}
class Form extends Component {
state = { isBlocking: false };
render() {
let { isBlocking } = this.state;
return (
<form
onSubmit={event => {
event.preventDefault();
event.target.reset();
this.setState({
isBlocking: false
});
}}
>
<Prompt
when={isBlocking}
message={location =>
`Are you sure you want to go to ${location.pathname}`
}
/>
<p>
Blocking?{" "}
{isBlocking ? "Yes, click a link or the back button" : "Nope"}
</p>
<p>
<input
size="50"
placeholder="type something to block transitions"
onChange={event => {
this.setState({
isBlocking: event.target.value.length > 0
});
}}
/>
</p>
<p>
<button>Submit to stop blocking</button>
</p>
</form>
);
}
}
export default PreventingTransitionsExample;
답변
history.listen 사용
예를 들면 아래와 같습니다.
구성 요소에서
componentWillMount() {
this.props.history.listen(() => {
// Detecting, user has changed URL
console.info(this.props.history.location.pathname);
});
}