Vraag Het detecteren van het verlaten van de gebruiker pagina met react-router


Ik wil dat mijn ReactJS-app een gebruiker op de hoogte brengt wanneer hij weg navigeert van een specifieke pagina. Specifiek een pop-upbericht dat hem / haar eraan herinnert om een ​​actie te ondernemen:

"Wijzigingen worden opgeslagen, maar nog niet gepubliceerd. Doe je dat nu?"

Moet ik dit activeren react-router globaal, of is dit iets dat gedaan kan worden vanuit de reactpagina / component?

Ik heb niets gevonden over het laatste en ik vermijd liever het eerste. Tenzij het natuurlijk de norm is, maar dat maakt dat ik me afvraag hoe ik zoiets kan doen zonder code toe te voegen aan elke andere pagina waar de gebruiker naar toe kan gaan ..

Alle inzichten welkom, bedankt!


24
2017-09-29 10:36


oorsprong


antwoorden:


Voor react-router 2.4.0+

NOTITIE: Het is raadzaam om al uw code naar het nieuwste te migreren react-router om alle nieuwe goodies te krijgen.

Zoals aanbevolen in de documentatie van de react-router:

Men zou de moeten gebruiken withRouter hogere orde component:

We denken dat deze nieuwe HoC leuker en gemakkelijker is, en deze zal gebruiken   documentatie en voorbeelden, maar het is geen moeilijke vereiste om dit te doen   schakelaar.

Als een voorbeeld van ES6 uit de documentatie:

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)

19
2018-06-21 07:18



In react-router v2.4.0 of hierboven en daarvoor v4 er zijn verschillende opties

  1. Functie toevoegen onLeave voor Route
 <Route
      path="/home"
      onEnter={ auth }
      onLeave={ showConfirm }
      component={ Home }
    >
  1. Gebruik de functie setRouteLeaveHook voor componentDidMount 

U kunt een overgang voorkomen of de gebruiker vragen om een ​​route met een leave hook te verlaten.

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?'
    },

    // ...

  })
)

Merk op dat dit voorbeeld gebruik maakt van de withRouter hogere orde component geïntroduceerd in v2.4.0.

Deze oplossing werkt echter niet helemaal perfect als je de route in de URL handmatig wijzigt

In die zin

  • we zien de bevestiging - oke
  • inhoud van pagina wordt niet opnieuw geladen - ok
  • URL verandert niet - niet goed

Echter in react-router v4 , het is gemakkelijker om te implementeren met behulp van Prompt from'react-router

Volgens de documentatie

prompt

Wordt gebruikt om de gebruiker te vragen voordat hij van een pagina weg navigeert. Wanneer je   toepassing voert een status in die de gebruiker moet voorkomen   weglopen (zoals een formulier voor de helft is ingevuld), render a <Prompt>.

import { Prompt } from 'react-router'

<Prompt
  when={formIsHalfFilledOut}
  message="Are you sure you want to leave?"
/>

bericht: string

Het bericht om de gebruiker te vragen wanneer ze weg proberen te navigeren.

<Prompt message="Are you sure you want to leave?"/>

bericht: func

Wordt aangeroepen met de volgende locatie en actie die de gebruiker is   proberen te navigeren naar. Retourneer een tekenreeks om een ​​prompt weer te geven voor de   gebruiker of waar om de overgang toe te staan.

<Prompt message={location => (
  `Are you sure you want to go to ${location.pathname}?`
)}/>

wanneer: bool

In plaats van een voorwaardelijke weergave van een <Prompt> achter een bewaker, jij   kan het altijd weergeven maar doorgeven when={true} of when={false} naar   voorkomen of toestaan ​​van navigatie dienovereenkomstig.

In uw weergavemethode hoeft u dit alleen maar toe te voegen zoals vermeld in de documentatie op basis van uw behoefte.

BIJWERKEN:

Als u een aangepaste actie wilt uitvoeren wanneer het gebruik de pagina verlaat, kunt u aangepaste geschiedenis gebruiken en uw router configureren zoals

history.js

import createBrowserHistory from 'history/createBrowserHistory'
export const history = createBrowserHistory()

... 
import { history } from 'path/to/history';
<Router history={history}>
  <App/>
</Router>

en dan in je component waar je gebruik van kunt maken history.blockgraag willen

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
   }
}

14
2017-08-24 19:24



react-router v4 introduceert een nieuwe manier om navigatie te blokkeren met Prompt. Voeg dit gewoon toe aan het onderdeel dat u wilt blokkeren:

import { Prompt } from 'react-router'

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

Hiermee wordt elke routering geblokkeerd, maar niet paginavernieuwing of sluiting. Om dit te blokkeren, moet u dit toevoegen (indien nodig bijwerken met de juiste React-levenscyclus):

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

onbeforeunload heeft verschillende ondersteuning door browsers.


9
2018-03-08 00:35



Voor react-router v0.13.x met react v0.13.x:

dit is mogelijk met de willTransitionTo() en willTransitionFrom() statische methoden. Zie mijn andere antwoord hieronder voor nieuwere versies.

Van de documentatie van de react-router:

U kunt een aantal statische methoden definiëren voor uw routeaanwijzingen die tijdens routetransities worden aangeroepen.

willTransitionTo(transition, params, query, callback)

Wordt aangeroepen wanneer een handler op het punt staat te worden weergegeven, waardoor u de mogelijkheid heeft om de overgang af te breken of om te leiden. Je kunt de transitie pauzeren terwijl je asynchroon werk doet en callback (fout) belt als je klaar bent, of de callback weglaten in je lijst met argumenten en deze zal voor je worden opgeroepen.

willTransitionFrom(transition, component, callback)

Wordt aangeroepen wanneer een actieve route wordt overgezet en u de gelegenheid biedt om de overgang af te breken. Het onderdeel is het huidige onderdeel, u zult het waarschijnlijk nodig hebben om de status te controleren om te beslissen of u de overgang wilt toestaan ​​(zoals formuliervelden).

Voorbeeld

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

    //...
  });

Voor react-router 1.0.0-rc1 met react v0.14.x of hoger:

dit zou mogelijk moeten zijn met de routerWillLeave levenscyclus haak. Voor oudere versies, zie mijn antwoord hierboven.

Van de documentatie van de react-router:

Om deze haak te installeren, gebruikt u de Lifecycle-mixin in een van uw routecomponenten.

  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?'
    },

    // ...

  })

Dingen. kan echter vóór de definitieve uitgave veranderen.


2
2017-09-30 06:57



Ik had hetzelfde probleem waarbij ik een bevestigingsbericht nodig had voor niet-opgeslagen wijzigingen op de pagina. In mijn geval gebruikte ik Reageer Router v3, dus ik kon het niet gebruiken <Prompt />, die is geïntroduceerd vanuit Reageer Router v4.

Ik pakte 'back button click' en 'accidentele link klik' aan met de combinatie van setRouteLeaveHook en history.pushState(), en behandeld 'reload-knop' met onbeforeunload gebeurtenishandler.

setRouteLeaveHook (dokter) & history.pushState (dokter)

  • Het gebruik van alleen setRouteLeaveHook was niet genoeg. Om een ​​of andere reden werd de URL gewijzigd, hoewel de pagina hetzelfde bleef toen op de knop 'Vorige' werd geklikt.

      // 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 (dokter)

  • Het wordt gebruikt om de knop 'per ongeluk opnieuw laden' af te handelen

    window.onbeforeunload = this.handleOnBeforeUnload;
    
    ...
    
    handleOnBeforeUnload = e => {
      const message = 'Are you sure?';
      e.returnValue = message;
      return message;
    }
    

Hieronder staat het volledige onderdeel dat ik heb geschreven

  • Let daar op withRouter is gebruikt om te hebben this.props.router.
  • Let daar op this.props.route wordt doorgegeven van de aanroepende component
  • Let daar op currentState wordt doorgegeven als prop om de initiële status te hebben en om elke verandering te controleren

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

Laat het me weten als er een vraag is :)


2
2018-05-17 09:34