प्रतिक्रिया-राउटर के साथ उपयोगकर्ता छोड़ने वाले पृष्ठ का पता लगाना


116

मैं एक विशिष्ट पृष्ठ से दूर नेविगेट करते समय एक उपयोगकर्ता को सूचित करने के लिए मेरा रिएक्टज एप्लिकेशन चाहता हूं। विशेष रूप से एक पॉपअप संदेश जो उसे क्रिया करने के लिए याद दिलाता है:

"परिवर्तन सहेजे गए हैं, लेकिन अभी तक प्रकाशित नहीं हुए हैं। क्या अब?"

क्या मुझे इसे react-routerविश्व स्तर पर ट्रिगर करना चाहिए , या यह कुछ ऐसा है जो प्रतिक्रिया पृष्ठ / घटक के भीतर से किया जा सकता है?

मैंने बाद में कुछ भी पाया, और मैं पहले से बचना चाहता था। जब तक इसके आदर्श के मानक नहीं हैं, लेकिन यह मुझे आश्चर्यचकित करता है कि हर दूसरे संभव पृष्ठ पर कोड जोड़ने के बिना ऐसा कैसे करना है जो उपयोगकर्ता जा सकता है ..

किसी भी अंतर्दृष्टि का स्वागत है, धन्यवाद!


3
मैं अब नहीं है अगर यह वह है जिसे आप खोज रहे हैं, लेकिन आप sth कर सकते हैं। इस तरह componentWillUnmount() { if (confirm('Changes are saved, but not published yet. Do that now?')) { // publish and go away from a specific page } else { // do nothing and go away from a specific page } }आप अपने प्रकाशन फ़ंक्शन को पेज छोड़ने के लिए कह सकते हैं
रिको बर्जर

जवाबों:


154

react-routerv4 नेविगेशन का उपयोग करके ब्लॉक करने के लिए एक नया तरीका पेश करता है 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>
)

यह किसी भी रूटिंग को ब्लॉक करेगा, लेकिन पेज रिफ्रेश या क्लोजिंग नहीं। इसे ब्लॉक करने के लिए, आपको इसे जोड़ना होगा (उपयुक्त रिएक्ट जीवनचक्र के साथ आवश्यकतानुसार अपडेट करना होगा):

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

onbeforeunload में ब्राउज़रों द्वारा विभिन्न समर्थन हैं।


9
हालांकि यह दो बहुत अलग दिखने वाले अलर्ट है।
XanderStrike

3
@XanderStrike आप ब्राउज़र के डिफ़ॉल्ट अलर्ट की नकल करने के लिए तुरंत अलर्ट को स्टाइल करने की कोशिश कर सकते हैं। दुर्भाग्य से, onberforeunloadअलर्ट को स्टाइल करने का कोई तरीका नहीं है ।
jcady

क्या उपयोगकर्ता द्वारा CANCEL बटन पर क्लिक करने के बाद एक ही प्रॉम्प्ट घटक दिखाने का कोई तरीका है?
रेने एनरिकेज़

1
@ReneEnriquez react-routerबॉक्स से बाहर का समर्थन नहीं करता है (रद्द बटन मानकर किसी भी मार्ग परिवर्तन को ट्रिगर नहीं करता है)। आप हालांकि व्यवहार की नकल करने के लिए अपना खुद का मॉडल बना सकते हैं।
जेसीडी

6
यदि आप उपयोग onbeforeunload करना समाप्त करते हैं , तो आप इसे तब साफ़ करना चाहेंगे जब आपका घटक अनमाउंट हो जाए। componentWillUnmount() { window.onbeforeunload = null; }
cdeutsch

40

प्रतिक्रिया-राउटर में 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 में मैन्युअल रूप से बदलते समय पूरी तरह से काम नहीं करता है

इस अर्थ में कि

  • हम पुष्टि करते हैं - ठीक है
  • पृष्ठ में पुनः लोड नहीं है - ठीक है
  • URL बदलता नहीं है - ठीक नहीं है

react-router v4शीघ्र या कस्टम इतिहास का उपयोग करने के लिए :

हालाँकि 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?"/>

संदेश: दुर्गंध

अगले स्थान के साथ बुलाया जाएगा और उपयोगकर्ता जिस पर नेविगेट करने का प्रयास कर रहा है। उपयोगकर्ता को संकेत दिखाने के लिए एक स्ट्रिंग लौटें या संक्रमण की अनुमति देने के लिए सही है।

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

कब: बूल

सशर्त एक प्रतिपादन करने के बजाय <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
   }
}

1
यदि आप <Prompt>URL का उपयोग करते हैं , तब भी जब आप प्रांप्ट पर रद्द करते हैं , तो URL बदल जाता है। संबंधित मुद्दा: github.com/ReactTraining/react-router/issues/5405
Sahan Serasinghe

1
मुझे लगता है कि यह सवाल अभी भी काफी लोकप्रिय है। चूंकि शीर्ष उत्तर पुराने पदावनत संस्करणों के बारे में हैं, इसलिए मैंने इस नए उत्तर को स्वीकृत के रूप में सेट किया है। पुराने प्रलेखन (और उन्नयन गाइड) के लिंक अब github.com/ReactTraining/react-router
बैरी Staes

1
onLeave को निकाल दिया जाता है, तो एक रूट परिवर्तन की पुष्टि की जाती है। क्या आप इस बारे में विस्तार से बता सकते हैं कि onLeave में नेविगेशन कैसे रद्द करें?
स्किंगेल

23

के लिए react-router2.4.0+

नोट : react-routerसभी नए माल प्राप्त करने के लिए अपने सभी कोड को नवीनतम पर स्थानांतरित करना उचित है ।

प्रतिक्रिया-राउटर प्रलेखन में अनुशंसित :

एक को withRouterउच्च क्रम के घटक का उपयोग करना चाहिए :

हमें लगता है कि यह नया एचओसी अच्छा और आसान है, और इसका उपयोग प्रलेखन और उदाहरणों में किया जाएगा, लेकिन इसे स्विच करने के लिए एक कठिन आवश्यकता नहीं है।

प्रलेखन से 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)

4
RouteLeaveHook कॉलबैक क्या करता है? क्या यह इन-बिल्ट मोडल का उपयोग कर उपयोगकर्ता को संकेत देता है? क्या होगा यदि आप एक कस्टम मोडल चाहते हैं
लर्नर

जैसे @Learner: vex या SweetAlert या अन्य गैर-अवरोधक संवाद जैसे अनुकूलित पुष्टिकरण बॉक्स के साथ यह कैसे करें?
ओलेफ्रांक

मुझे निम्नलिखित त्रुटि हो रही है: - TypeError: अपरिभाषित की संपत्ति ' आईडी ' नहीं पढ़ सकता है । कोई सुझाव
मुस्तफा मामून

@MustafaMamun एक असंबंधित समस्या लगती है और आप ज्यादातर विवरणों को समझाते हुए एक नया प्रश्न बनाना चाहेंगे।
स्निम्कप्रपत्ति

मैं इस मुद्दे को हल करने की कोशिश कर रहा था। घटक को सीधे राउटर से जोड़ा जाना है फिर यह समाधान काम करता है। मुझे एक बेहतर उत्तर मिला है stackoverflow.com/questions/39103684/…
मुस्तफा मामून

4

के लिए react-routerv3.x

मेरे पास वही मुद्दा था जहां मुझे पृष्ठ पर किसी भी सहेजे नहीं गए परिवर्तन के लिए पुष्टिकरण संदेश की आवश्यकता थी। मेरे मामले में, मैं रिएक्ट राउटर v3 का उपयोग कर रहा था , इसलिए मैं उपयोग नहीं कर सका <Prompt />, जिसे रिएक्ट राउटर v4 से पेश किया गया था ।

मैं के संयोजन के साथ 'वापस बटन क्लिक' और 'आकस्मिक लिंक क्लिक' को संभाला 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);

अगर कोई सवाल है तो कृपया मुझे बताएं :)


वास्तव में यह कहां से है ।formStartEle कहां से आ रहा है?
गौरव

2

के लिए react-routerसाथ v0.13.x reactv0.13.x:

यह willTransitionTo()और willTransitionFrom()स्थैतिक तरीकों से संभव है । नए संस्करणों के लिए, नीचे मेरा अन्य उत्तर देखें।

से प्रतिक्रिया रूटर प्रलेखन :

आप अपने मार्ग संचालकों पर कुछ स्थिर विधियों को परिभाषित कर सकते हैं जिन्हें मार्ग परिवर्तन के दौरान कहा जाएगा।

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-routerके साथ 1.0.0-RC1 reactv0.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?'
    },

    // ...

  })

हालात। हालांकि अंतिम रिलीज से पहले बदल सकता है।


1

आप इस प्रॉम्प्ट का उपयोग कर सकते हैं।

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;

1

History.listen का उपयोग करना

नीचे दिए गए उदाहरण के लिए:

आपके घटक में,

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

5
नमस्ते, ढेर अतिप्रवाह में आपका स्वागत है। जब किसी प्रश्न का उत्तर दिया जा रहा हो, जिसमें पहले से ही कई उत्तर हों, तो कृपया कुछ अतिरिक्त जानकारी जोड़ना सुनिश्चित करें कि जो प्रतिक्रिया आप प्रदान कर रहे हैं, वह मूल क्यों है और क्या मूल पोस्टर द्वारा पहले ही वीटो लगा दिया गया है। यह विशेष रूप से "कोड-ओनली" उत्तरों में महत्वपूर्ण है जैसे कि आपने जो प्रदान किया है।
चब
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.