it-source

리액트 라우터를 사용하여 사용자가 페이지를 떠나는 것을 감지하는 중

criticalcode 2023. 3. 7. 21:34
반응형

리액트 라우터를 사용하여 사용자가 페이지를 떠나는 것을 감지하는 중

내 ReactJS 앱이 특정 페이지에서 벗어날 때 사용자에게 알렸으면 합니다.특히 작업을 수행하도록 알려주는 팝업 메시지:

변경 내용은 저장되지만 아직 공개되지 않았습니다.지금 당장 할까?

를 거거 this this에서 요?react-router또는 리액트 페이지/컴포넌트 내에서 실행할 수 있습니까?

후자에 대해선 아무것도 못 찾았고, 첫 번째는 피하고 싶어요물론 그것이 일반적인 것이 아니라면, 어떻게 하면 사용자가 갈 수 있는 다른 모든 페이지에 코드를 추가하지 않고 이런 일을 할 수 있을지 궁금하게 된다.

어떤 통찰이라도 환영합니다, 감사합니다!

react-router는 v4를 사용하여 했습니다.Prompt차단할 컴포넌트에 다음 내용을 추가합니다.

import { Prompt } from 'react-router'

const MyComponent = () => (
  <>
    <Prompt
      when={shouldBlockNavigation}
      message='You have unsaved changes, are you sure you want to leave?'
    />
    {/* Component JSX */}
  </>
)

그러면 라우팅이 차단되지만 페이지 새로 고침이나 닫힘은 차단되지 않습니다.이를 차단하려면 다음을 추가해야 합니다(필요에 따라 적절한 React 라이프 사이클로 업데이트).

componentDidUpdate = () => {
  if (shouldBlockNavigation) {
    window.onbeforeunload = () => true
  } else {
    window.onbeforeunload = undefined
  }
}

onbeforeunload는 브라우저별로 다양한 지원을 제공합니다.

라우터의 v2.4.0 그 또는 그 이전v4 가지 가 있다

  1. 추가 " " "onLeave★★★★★★에Route
 <Route
      path="/home"
      onEnter={ auth }
      onLeave={ showConfirm }
      component={ Home }
    >
    
  1. 사용setRouteLeaveHook★★★★★★에componentDidMount

이행을 방지하거나 사용자에게 프롬프트를 표시한 후 탈퇴 후크를 사용하여 루트를 떠날 수 있습니다.

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의 루트를 수동으로 변경할 때는 이러한 솔루션이 완벽하게 동작하지 않습니다.

그런 의미에서

  • Confirmation - OK가 표시됩니다.
  • 페이지 포함이 새로고침되지 않음 - OK
  • URL이 변경되지 않음 - 괜찮지 않음

★★★의 react-router v4"CHANGE: "CHANGE: "CHANGE: " 。

, ,에서는react-router v4from'to-interminal의 도움을 받아 구현이 비교적 용이합니다.

서류에 의하면

신속한

페이지에서 이동하기 전에 사용자에게 묻기 위해 사용됩니다. 할 수 있는 처럼), 「」를 .<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.disclosing

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-router2.4.0+

메모: 모든 코드를 최신 버전으로 이행하여 모든 새로운 제품을 입수하는 것이 좋습니다.

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-router1993.x.

페이지에 저장되지 않은 변경에 대한 확인 메시지가 필요한 것과 같은 문제가 있었습니다.제 경우 리액트 라우터 v3를 사용하고 있었기 때문에<Prompt />React Router v4에서 도입되었습니다.

' 클릭과 ' 클릭'을 '뒤로 버튼 클릭'과 '우발 링크 클릭했습니다.setRouteLeaveHook ★★★★★★★★★★★★★★★★★」history.pushState() '은 '버튼'을 '버튼'으로 처리했습니다.onbeforeunload이벤트 핸들러

setRouteLeaveHook(doc) history.pushState(doc)

  • set Route Leave Hook만 사용하는 것만으로는 충분하지 않았습니다.'뒤로 버튼'을 클릭했을 때 페이지는 그대로였지만, 어떤 이유로 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;
        }
      };
    

언로드 전(doc)

  • '우발적인 새로고침' 버튼을 처리하는 데 사용됩니다.

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

질문이 있으시면 연락주세요.

이렇게 하면 사용자가 다른 경로로 전환하거나 현재 페이지를 떠나 다른 URL로 이동할 때 메시지를 표시할 수 있습니다.

import PropTypes from 'prop-types'
import React, { useEffect } from 'react'
import { Prompt } from 'react-router-dom'
import { useTranslation } from 'react-i18next'


const LeavePageBlocker = ({ when }) => {
  const { t } = useTranslation()
  const message = t('page_has_unsaved_changes')

  useEffect(() => {
    if (!when) return () => {}

    const beforeUnloadCallback = (event) => {
      event.preventDefault()
      event.returnValue = message
      return message
    }

    window.addEventListener('beforeunload', beforeUnloadCallback)
    return () => {
      window.removeEventListener('beforeunload', beforeUnloadCallback)
    }
  }, [when, message])

  return <Prompt when={when} message={message} />
}

LeavePageBlocker.propTypes = {
  when: PropTypes.bool.isRequired,
}

export default LeavePageBlocker

페이지:

const [dirty, setDirty] = setState(false)
...
return (
  <>
    <LeavePageBlocker when={dirty} />
    ...
  </>
)

history.listen 사용

예를 들어 다음과 같습니다.

컴포넌트에서는

componentWillMount() {
    this.props.history.listen(() => {
      // Detecting, user has changed URL
      console.info(this.props.history.location.pathname);
    });
}

★★★의 react-router 와 v0.13.x ®react v0.13.x:

은 「 」를 사용하면 가능합니다.willTransitionTo() ★★★★★★★★★★★★★★★★★」willTransitionFrom()는 다음 해 주세요.새로운 버전의 경우는, 다음의 답변을 참조해 주세요.

react-router 매뉴얼에서 다음 항목을 참조하십시오.

루트 전환 중에 호출되는 몇 가지 스태틱메서드를 루트핸들러로 정의할 수 있습니다.

willTransitionTo(transition, params, query, callback)

핸들러가 렌더하려고 하면 호출되며, 전환을 중단하거나 리디렉션할 수 있습니다.비동기 작업 중 이행을 일시 정지하거나 콜백(오류)을 완료하면 콜백을 인수 목록에서 생략하면 콜백이 호출됩니다.

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-router1.0.0-rc1과reactv0.14.x 이후:

이것은, 다음의 방법으로 실현될 수 있습니다.routerWillLeave라이프 사이클 훅이전 버전의 경우 위의 답변을 참조하십시오.

react-router 매뉴얼에서 다음 항목을 참조하십시오.

이 후크를 설치하려면 루트 컴포넌트 중 하나에 라이프 사이클 믹스인을 사용합니다.

  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;

import React, {useEffect} from 'react';
import { useLocation} from 'react-router';


const prevLocation = useLocation().pathname; 

useEffect(() => {
  const unlisten = history.listen((location) => {
    if (unsavedCondition && location.pathname !== prevLocation) {
      history.push(prevLocation) 
      
      //Do something, like display confirmation dialog!
    }
  });

  return () => {
    unlisten()
  }
},[])

사용할 수 있습니다.componentWillUnmount()사용자가 페이지를 떠나기 전에 어떤 작업도 수행합니다.기능 컴포넌트를 사용하고 있는 경우는, 다음과 같이 할 수 있습니다.useEffect()훅. 훅은 훅을 반환하는 함수를 받아들입니다.Destructor이것은, 다음과 같습니다.componentWillUnmount()할 수 있어요.

이 물건은 신용이 있다

react-router v3.x 및 기능 컴포넌트의 경우 다음과 같은 후크를 사용할 수 있습니다.

import { useEffect, useState } from "react";
import { usePrevious } from "./usePrevious";

const useConfirmation = ({ router, route, items }) => {
  const [needConfirmation, setNeedConfirmation] = useState(false);
// You can use for the prevState any value, in my case it was length of items
      const prevItemsLength = usePrevious({ length: items?.length });
    
  const commonMsg = 
    "you-have-unsaved-information-are-you-sure-you-want-to-leave-this-page";

  const onBeforeUnload = (e) => {
    if (needConfirmation) {
      e.returnValue = true;
      return commonMsg;
    }

    return null;
  };

  const routerWillLeave = () => {
    if (needConfirmation) {
      return commonMsg;
    }

    return true;
  };

  useEffect(() => {
    if (prevItemsLength?.length > items?.length) {
      setNeedConfirmation(() => true);
    }
  }, [items]);

  useEffect(() => {
    if (needConfirmation) {
      window.addEventListener("beforeunload", onBeforeUnload);
      router.setRouteLeaveHook(route, routerWillLeave);
    } else {
      router.setRouteLeaveHook(route, () => {});
      window.removeEventListener("beforeunload", onBeforeUnload);
    }

    return () => window.removeEventListener("beforeunload", onBeforeUnload);
  }, [needConfirmation]);

  return [needConfirmation, setNeedConfirmation];
};

export { useConfirmation };

그런 다음 다른 파일에서는 데이터가 저장된 후 확인을 비활성화합니다.

  const [needConfirm, setNeedConfirm] = useConfirmation({
    router,
    route,
    items,
  });

  const saveChanges = useCallback(() => {
    //before turning off confirmation, there may be a request to the API to save our data
    //if request was success then we set the 'needConfirm' value to 'false'
    setNeedConfirm(() => false);
  }); 

usePrevious' 후크에 대한 정보는 다음과 같습니다.반응 후크의 oldValues와 newValues를 비교하는 방법 useEffect?

언급URL : https://stackoverflow.com/questions/32841757/detecting-user-leaving-page-with-react-router

반응형