प्रतिक्रिया राउटर v4 में मार्ग परिवर्तनों को कैसे सुनें?


138

मेरे पास कुछ बटन हैं जो मार्गों के रूप में कार्य करते हैं। हर बार मार्ग बदल दिया जाता है, मैं यह सुनिश्चित करना चाहता हूं कि बटन सक्रिय परिवर्तन है।

क्या प्रतिक्रिया राउटर v4 में मार्ग परिवर्तनों को सुनने का एक तरीका है?


जवाबों:


170

मैं सहारा withRouterपाने के लिए उपयोग करता हूं location। जब घटक एक नए मार्ग के कारण अपडेट किया जाता है, तो मैं जाँचता हूं कि क्या मूल्य बदल गया है:

@withRouter
class App extends React.Component {

  static propTypes = {
    location: React.PropTypes.object.isRequired
  }

  // ...

  componentDidUpdate(prevProps) {
    if (this.props.location !== prevProps.location) {
      this.onRouteChanged();
    }
  }

  onRouteChanged() {
    console.log("ROUTE CHANGED");
  }

  // ...
  render(){
    return <Switch>
        <Route path="/" exact component={HomePage} />
        <Route path="/checkout" component={CheckoutPage} />
        <Route path="/success" component={SuccessPage} />
        // ...
        <Route component={NotFound} />
      </Switch>
  }
}

आशा करता हूँ की ये काम करेगा


21
प्रतिक्रिया रूटर v4 में 'this.props.location.pathname' का उपयोग करें।
ptorsson

4
@ledfusion मैं वही कर रहा हूं और उपयोग कर withRouterरहा हूं, लेकिन मुझे त्रुटि मिल रही है You should not use <Route> or withRouter() outside a <Router>। मुझे <Router/>उपरोक्त कोड में कोई घटक नहीं दिख रहा है । तो यह कैसे काम कर रहा है?
आवारा

1
हाय @maverick। मुझे यकीन नहीं है कि आपका कोड कैसा दिखता है, लेकिन ऊपर दिए गए उदाहरण में, <Switch>घटक डी-फैक्टो राउटर के रूप में काम कर रहा है। <Route>मिलान पथ के लिए केवल पहली प्रविष्टि, प्रदान की जाएगी। <Router/>इस परिदृश्य में किसी भी घटक की आवश्यकता नहीं है
9

1
@withRouter का उपयोग करने के लिए आपको npm स्थापित करने की आवश्यकता है - सेव-देव ट्रांसफॉर्म-डेकोरेटर्स-लीगेसी
सिगैक्स

68

उपरोक्त पर विस्तार करने के लिए, आपको इतिहास ऑब्जेक्ट पर प्राप्त करना होगा। यदि आप उपयोग कर रहे हैं BrowserRouter, तो आप इतिहास घटक के गुणों और कार्यों के लिए प्रॉप्स के माध्यम से एक्सेस करने के लिए withRouterअपने घटक को उच्च-ऑर्डर घटक (HoC) के साथ आयात और लपेट सकते हैं ।

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

const myComponent = ({ history }) => {

    history.listen((location, action) => {
        // location is an object like window.location
        console.log(action, location.pathname, location.state)
    });

    return <div>...</div>;
};

export default withRouter(myComponent);

केवल इस बात की जानकारी होनी चाहिए कि प्रॉप को अप्रोच करने के लिए विथ राउटर और अधिकांश अन्य तरीके हैं historyक्योंकि वे ऑब्जेक्ट को डी-स्ट्रक्चर करते हैं।


उत्तर ने मुझे प्रश्न की परवाह किए बिना कुछ समझने में मदद की :)। लेकिन तय withRoutesकर लो withRouter
सर्गेई रुतस्की

हां, क्षमा करें, यह बताने के लिए धन्यवाद। मैंने पोस्ट में संशोधन किया है। मैंने सवाल के शीर्ष पर सही आयात किया और फिर इसे कोड उदाहरण में गलत वर्तनी दी।
सैम परमेस्टर

5
मुझे लगता है कि वेरिएबल के बजाय विथड्रॉप का वर्तमान संस्करण गुजरता है । historylisten
माइकब्रिज

5
असावधान प्रदर्शित करने के लिए पद में संशोधन करना अच्छा होगा; इस कोड में एक मेमोरी लीक है।
एंड्रयूसपॉव

34

आपको इतिहास v4 lib का उपयोग करना चाहिए ।

वहां से उदाहरण

history.listen((location, action) => {
  console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`)
  console.log(`The last navigation action was ${action}`)
})

1
History.pushState () और history.replaceState () कॉल पॉपस्टैट ईवेंट को ट्रिगर नहीं करते हैं, इसलिए यह अकेले सभी मार्ग परिवर्तनों को कवर नहीं करेगा।
रयान

1
@ रियान ऐसा लगता है कि history.pushट्रिगर करता है history.listenइतिहास में आधार URL उदाहरण का उपयोग करके देखें v4 डॉक्स । क्योंकि historyयह वास्तव historyमें एक ब्राउज़र की मूल वस्तु का आवरण है, यह मूल निवासी की तरह व्यवहार नहीं करता है।
रॉकलाईट

यह एक बेहतर समाधान की तरह लगता है, जितनी बार आपको ईवेंट पुशिंग के लिए मार्ग परिवर्तनों को सुनने की आवश्यकता होती है, जो घटक जीवन चक्र घटनाओं पर प्रतिक्रिया करने के लिए असंबंधित होती है।
डैनियल डुबोव्स्की जूल

12
संभावित स्मृति रिसाव! बहुत महत्वपूर्ण है कि आप ऐसा करते हैं! "जब आप history.listen का उपयोग करते हुए एक श्रोता को संलग्न करते हैं, तो यह एक ऐसा फ़ंक्शन देता है जिसका उपयोग श्रोता को निकालने के लिए किया जा सकता है, जिसे बाद में सफाई तर्क में आमंत्रित किया जा सकता है:const unlisten = history.listen(myListener); unlisten();
Dehan de Croos

इतिहास पैकेज पर प्रलेखन के लिए यहां जाएं। github.com/ReactTraining/history/blob/master/docs/…
जेसन किम

26

withRouter, history.listenऔर useEffect(रिएक्ट हुक) एक साथ काफी अच्छी तरह से काम करता है:

import React, { useEffect } from 'react'
import { withRouter } from 'react-router-dom'

const Component = ({ history }) => {
    useEffect(() => history.listen(() => {
        // do something on route change
        // for my example, close a drawer
    }), [])

    //...
}

export default withRouter(Component)

श्रोता कॉलबैक किसी भी समय एक मार्ग को बदल देगा, और वापसी history.listenएक शटडाउन हैंडलर है जो अच्छी तरह से खेलता है useEffect


13

v5.1 उपयोगी हुक का परिचय देता है useLocation

https://reacttraining.com/blog/react-router-v5-1/#uselocation

import { Switch, useLocation } from 'react-router-dom'

function usePageViews() {
  let location = useLocation()

  useEffect(
    () => {
      ga.send(['pageview', location.pathname])
    },
    [location]
  )
}

function App() {
  usePageViews()
  return <Switch>{/* your routes here */}</Switch>
}

4
बस एक नोट के रूप में मैं एक त्रुटि के साथ परेशान था Cannot read property 'location' of undefined at useLocation:। आपको यह सुनिश्चित करने की आवश्यकता है कि यूटीलोकेशन () कॉल उसी घटक में नहीं है जो राउटर को पेड़ में डालता है: यहां देखें
टॉड

11

हुक के साथ:

import { useEffect } from 'react'
import { withRouter } from 'react-router-dom'
import { history as historyShape } from 'react-router-prop-types'

const DebugHistory = ({ history }) => {
  useEffect(() => {
    console.log('> Router', history.action, history.location])
  }, [history.location.key])

  return null
}

DebugHistory.propTypes = { history: historyShape }

export default withRouter(DebugHistory)

आयात और <DebugHistory>घटक के रूप में प्रस्तुत करना


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

function MyApp() {

  const location = useLocation();

  useEffect(() => {
      console.log('route has been changed');
      ...your code
  },[location.pathname]);

}

हुक के साथ


होली जेसिस! यह कैसे काम करता है? आपका जवाब अच्छा है! बूटी में डिबगर बिंदु डाल useEffect लेकिन किसी भी समय मैं पथ नाम स्थान बदल दिया रुके अपरिभाषित ? क्या आप कोई अच्छा लेख साझा कर सकते हैं? यह किसी भी स्पष्ट जानकारी को खोजने के लिए मुश्किल है
एलेक्सनिकोव

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

const Scroll = () => {
  const history = useHistory();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [history.location.pathname]);

  return null;
}

क्या यह भी हैश परिवर्तन देखता है? मार्ग / एक # १ -> मार्ग / एक # २
नरेन

1

प्रतिक्रिया हुक्स के साथ, मैं उपयोग कर रहा हूं useEffect

  const history = useHistory()
  const queryString = require('query-string')
  const parsed = queryString.parse(location.search)
  const [search, setSearch] = useState(parsed.search ? parsed.search : '')

  useEffect(() => {
    const parsedSearch = parsed.search ? parsed.search : ''
    if (parsedSearch !== search) {
      // do some action! The route Changed!
    }
  }, [location.search])

0

कुछ मामलों में आप इस तरह से renderइसके बजाय विशेषता का उपयोग कर सकते हैं component:

class App extends React.Component {

    constructor (props) {
        super(props);
    }

    onRouteChange (pageId) {
        console.log(pageId);
    }

    render () {
        return  <Switch>
                    <Route path="/" exact render={(props) => { 
                        this.onRouteChange('home');
                        return <HomePage {...props} />;
                    }} />
                    <Route path="/checkout" exact render={(props) => { 
                        this.onRouteChange('checkout');
                        return <CheckoutPage {...props} />;
                    }} />
                </Switch>
    }
}

ध्यान दें कि यदि आप onRouteChangeविधि में स्थिति बदलते हैं , तो यह 'अधिकतम अद्यतन गहराई से अधिक' त्रुटि का कारण हो सकता है।


0

useEffectहुक के साथ श्रोता को जोड़े बिना मार्ग परिवर्तनों का पता लगाना संभव है।

import React, { useEffect }           from 'react';
import { Switch, Route, withRouter }  from 'react-router-dom';
import Main                           from './Main';
import Blog                           from './Blog';


const App  = ({history}) => {

    useEffect( () => {

        // When route changes, history.location.pathname changes as well
        // And the code will execute after this line

    }, [history.location.pathname]);

    return (<Switch>
              <Route exact path = '/'     component = {Main}/>
              <Route exact path = '/blog' component = {Blog}/>
            </Switch>);

}

export default withRouter(App);

0

मैं अभी इस समस्या से निपटता हूं, इसलिए मैं दिए गए अन्य उत्तरों पर एक पूरक के रूप में अपना समाधान जोड़ूंगा।

यहाँ समस्या यह है कि useEffectवास्तव में यह काम नहीं करता है जैसा कि आप इसे चाहते हैं, क्योंकि कॉल केवल पहले रेंडर के बाद चालू हो जाता है इसलिए अवांछित देरी होती है।
यदि आप कुछ राज्य प्रबंधक जैसे Redux का उपयोग करते हैं, तो संभावना है कि आपको स्टोर में स्थिति की स्थिति के कारण स्क्रीन पर एक फ़्लिकर मिलेगा।

क्या आप वास्तव में चाहते हैं का उपयोग करने के लिए है useLayoutEffectक्योंकि यह तुरंत ट्रिगर हो जाता है।

इसलिए मैंने एक छोटी उपयोगिता फ़ंक्शन लिखा, जिसे मैंने अपने राउटर के समान निर्देशिका में रखा:

export const callApis = (fn, path) => {
    useLayoutEffect(() => {
      fn();
    }, [path]);
};

जिसे मैं इस तरह घटक एचओसी के भीतर से कॉल करता हूं:

callApis(() => getTopicById({topicId}), path);

pathवह प्रोप है जो matchउपयोग करते समय ऑब्जेक्ट में पास हो जाता है withRouter

मैं वास्तव में इतिहास पर मैन्युअल रूप से सुनने / गैर-सूचीबद्ध होने के पक्ष में नहीं हूं। वह सिर्फ इमो है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.