प्रतिक्रिया-राउटर के साथ मार्ग परिवर्तन का पता लगाएं


87

मुझे ब्राउज़िंग इतिहास के आधार पर कुछ व्यावसायिक तर्क लागू करने होंगे।

मैं ऐसा कुछ करना चाहता हूं:

reactRouter.onUrlChange(url => {
   this.history.push(url);
});

क्या URL अपडेट होने पर प्रतिक्रिया-राउटर से कॉलबैक प्राप्त करने का कोई तरीका है?


प्रतिक्रिया-राउटर के किस संस्करण का आप उपयोग कर रहे हैं? यह सबसे अच्छा तरीका निर्धारित करेगा। आपके अपडेट के बाद मैं एक उत्तर प्रदान करूंगा। कहा जा रहा है, एक घटक स्थान के बारे में जागरूक करने के लिए withRouter HoC शायद आपकी सबसे अच्छी शर्त है। यह आपके घटक को नए ({मैच, इतिहास और स्थान}) से अपडेट करेगा, जब भी कोई रूट बदलता है। इस तरह आपको मैन्युअल रूप से सदस्यता लेने और ईवेंट के लिए सदस्यता समाप्त करने की आवश्यकता नहीं है। मतलब कार्यात्मक स्टेटलेस घटकों के साथ-साथ कक्षा घटकों का उपयोग करना आसान है।
काइल रिचर्डसन

जवाबों:


114

history.listen()मार्ग परिवर्तन का पता लगाने का प्रयास करते समय आप फ़ंक्शन का उपयोग कर सकते हैं। ध्यान में रखते हुए कि आप उपयोग कर रहे हैं react-router v4, अपने घटक को withRouterHOC के साथ लपेटें ताकि historyप्रोप का उपयोग हो सके ।

history.listen()एक unlistenफ़ंक्शन देता है। आप इसे unregisterसुनने से उपयोग करेंगे ।

आप अपने मार्गों को जैसे कॉन्फ़िगर कर सकते हैं

index.js

ReactDOM.render(
      <BrowserRouter>
            <AppContainer>
                   <Route exact path="/" Component={...} />
                   <Route exact path="/Home" Component={...} />
           </AppContainer>
        </BrowserRouter>,
  document.getElementById('root')
);

और फिर AppContainer.js में

class App extends Component {
  
  componentWillMount() {
    this.unlisten = this.props.history.listen((location, action) => {
      console.log("on route change");
    });
  }
  componentWillUnmount() {
      this.unlisten();
  }
  render() {
     return (
         <div>{this.props.children}</div>
      );
  }
}
export default withRouter(App);

इतिहास डॉक्स से :

आप वर्तमान स्थान में परिवर्तन का उपयोग करके सुन सकते हैं history.listen:

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

स्थान ऑब्जेक्ट सहित window.location इंटरफ़ेस का एक सबसेट लागू करता है:

**location.pathname** - The path of the URL
**location.search** - The URL query string
**location.hash** - The URL hash fragment

स्थानों में निम्नलिखित गुण भी हो सकते हैं:

location.state - इस स्थान के लिए कुछ अतिरिक्त स्थिति जो URL में नहीं रहती है (समर्थित ( createBrowserHistoryऔर createMemoryHistory)

location.key- एक अद्वितीय इस स्थान का प्रतिनिधित्व स्ट्रिंग (में समर्थित createBrowserHistoryऔर createMemoryHistory)

क्रिया PUSH, REPLACE, or POPइस बात पर निर्भर करती है कि उपयोगकर्ता को वर्तमान URL पर कैसे मिला।

जब आप प्रतिक्रिया-राउटर v3 का उपयोग कर रहे हैं, तो आप पैकेज history.listen()से उपयोग कर सकते हैं historyजैसा कि ऊपर उल्लेख किया गया है या आप उपयोग भी कर सकते हैंbrowserHistory.listen()

आप अपने मार्गों को कॉन्फ़िगर और उपयोग कर सकते हैं जैसे

import {browserHistory} from 'react-router';

class App extends React.Component {

    componentDidMount() {
          this.unlisten = browserHistory.listen( location =>  {
                console.log('route changes');
                
           });
      
    }
    componentWillUnmount() {
        this.unlisten();
     
    }
    render() {
        return (
               <Route path="/" onChange={yourHandler} component={AppContainer}>
                   <IndexRoute component={StaticContainer}  />
                   <Route path="/a" component={ContainerA}  />
                   <Route path="/b" component={ContainerB}  />
            </Route>
        )
    }
} 

वह v3 का उपयोग कर रहा है और आपके उत्तर का दूसरा वाक्य कहता है " आपreact-router v4 जिस पर विचार कर रहे हैं "
काइल रिचर्डसन

1
@KyleRichardson मुझे लगता है कि आपने मुझे फिर से गलत समझा, मुझे निश्चित रूप से अपनी अंग्रेजी पर काम करना होगा। मेरा मतलब था कि यदि आप प्रतिक्रिया-राउटर v4 का उपयोग कर रहे हैं और आप इतिहास वस्तु का उपयोग कर रहे हैं, तो आपको अपने घटक को लपेटने की आवश्यकता हैwithRouter
शुभम खत्री

@KyleRichardson मैं आपको मेरा पूरा उत्तर देखता हूं, मैंने इसे v3 में भी करने के तरीके जोड़े हैं। एक और बात, ओपी ने टिप्पणी की कि वह आज v3 का उपयोग कर रहा है और मैंने कल इस सवाल पर जवाब दिया था
शुभम खत्री

1
@ शुभम खत्री ने हां लेकिन जिस तरह से आपका जवाब पढ़ा है वह गलत है। वह v4 का उपयोग नहीं कर रहा है ... इसके अलावा, हर बार राउटिंग होने पर नए प्रॉप्स के साथ अपने कंपोनेंट को अपडेट करने के history.listen()बाद आप इसका उपयोग क्यों करेंगे withRouter? आप का एक सरल तुलना कर सकता है nextProps.location.href === this.props.location.hrefमें componentWillUpdateकुछ भी आप अगर यह बदल गया है क्या करने की जरूरत करने के लिए।
काइल रिचर्डसन

1
@ क्रिस, क्या आपको इसे आजमाने के लिए बदलाव मिला है
शुभम खत्री

35

रिएक्टर राउटर 5.1 के लिए अपडेट।

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

const App = () => {
  const location = useLocation();

  React.useEffect(() => {
    console.log('Location changed');
  }, [location]);

  return (
    <Switch>
      {/* Routes go here */}
    </Switch>
  );
};

13

यदि आप historyविश्व स्तर पर ऑब्जेक्ट सुनना चाहते हैं , तो आपको इसे स्वयं बनाना होगा और इसे पास करना होगा Router। तो आप इसे इसकी listen()विधि के साथ सुन सकते हैं :

// Use Router from react-router, not BrowserRouter.
import { Router } from 'react-router';

// Create history object.
import createHistory from 'history/createBrowserHistory';
const history = createHistory();

// Listen to history changes.
// You can unlisten by calling the constant (`unlisten()`).
const unlisten = history.listen((location, action) => {
  console.log(action, location.pathname, location.state);
});

// Pass history to Router.
<Router history={history}>
   ...
</Router>

इससे भी बेहतर अगर आप एक मॉड्यूल के रूप में इतिहास ऑब्जेक्ट बनाते हैं, तो आप इसे आसानी से आयात कर सकते हैं कहीं भी आपको इसकी आवश्यकता हो सकती है (जैसे import history from './history';


9

react-router v6

आगामी v6 में , इसे useLocationऔर useEffectहुक के संयोजन से किया जा सकता है

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

const MyComponent = () => {
  const location = useLocation()

  React.useEffect(() => {
    // runs on location, i.e. route, change
    console.log('handle route change here', location)
  }, [location])
  ...
}

सुविधाजनक पुन: उपयोग के लिए, आप इसे कस्टम useLocationChangeहुक में कर सकते हैं

// runs action(location) on location, i.e. route, change
const useLocationChange = (action) => {
  const location = useLocation()
  React.useEffect(() => { action(location) }, [location])
}

const MyComponent1 = () => {
  useLocationChange((location) => { 
    console.log('handle route change here', location) 
  })
  ...
}

const MyComponent2 = () => {
  useLocationChange((location) => { 
    console.log('and also here', location) 
  })
  ...
}

यदि आपको परिवर्तन पर पिछले मार्ग को देखने की आवश्यकता है, तो आप usePreviousहुक के साथ संयोजन कर सकते हैं

const usePrevious(value) {
  const ref = React.useRef()
  React.useEffect(() => { ref.current = value })

  return ref.current
}

const useLocationChange = (action) => {
  const location = useLocation()
  const prevLocation = usePrevious(location)
  React.useEffect(() => { 
    action(location, prevLocation) 
  }, [location])
}

const MyComponent1 = () => {
  useLocationChange((location, prevLocation) => { 
    console.log('changed from', prevLocation, 'to', location) 
  })
  ...
}

यह ध्यान रखना महत्वपूर्ण है कि पहले क्लाइंट मार्ग पर उपरोक्त सभी आग , साथ ही बाद में परिवर्तन। यदि यह समस्या है, तो बाद के उदाहरण का उपयोग करें और जांचें कि prevLocationकुछ भी करने से पहले मौजूद है।


मेरा एक सवाल है। यदि कई घटक प्रदान किए गए हैं और वे सभी उपयोग देख रहे हैं तो उनके सभी उपयोग ट्रिगर हो जाएंगे। मैं कैसे सत्यापित करूं कि यह स्थान उस विशिष्ट घटक के लिए सही है जिसे प्रदर्शित किया जाएगा?
किक्स

1
अरे @Kex - locationयहाँ स्पष्ट करने के लिए ब्राउज़र स्थान है, इसलिए यह हर घटक में समान है और हमेशा इस अर्थ में सही है। यदि आप अलग-अलग घटकों में हुक का उपयोग करते हैं, तो स्थान बदलने पर वे सभी समान मान प्राप्त करेंगे। मुझे लगता है कि वे उस जानकारी के साथ क्या करते हैं वह अलग होगा, लेकिन यह हमेशा सुसंगत है।
डावनविलविल

यह समझ आता है। बस यह सोचकर कि एक घटक को कैसे पता चलेगा कि स्थान परिवर्तन अपने आप में एक कार्रवाई करने के लिए प्रासंगिक है। जैसे एक घटक डैशबोर्ड / सूची प्राप्त करता है लेकिन यह कैसे पता चलता है कि यह उस स्थान से जुड़ा हुआ है या नहीं?
Kex

जब तक मैं ऐसा कुछ नहीं करता, अगर (स्थान.पाथनाम === "डैशबोर्ड / सूची") {..... क्रियाएँ}। हालांकि यह एक घटक के लिए बहुत ही सुंदर हार्डकोडिंग पथ नहीं लगता है।
Kex

8

यह एक पुराना प्रश्न है और मैं मार्ग परिवर्तन के लिए मार्ग परिवर्तन के लिए सुनने की व्यावसायिक आवश्यकता को नहीं समझता; गोल चक्कर लगता है।

लेकिन अगर आप यहाँ समाप्त हो गए हैं क्योंकि आप सभी चाहते थे कि 'page_path'गूगल एनालिटिक्स / ग्लोबल साइट टैग / कुछ इसी तरह के रिएक्शन-राउटर रूट में बदलाव किया जाए, तो यहां एक हुक का उपयोग किया जा सकता है। मैंने इसे स्वीकृत उत्तर के आधार पर लिखा है:

useTracking.js

import { useEffect } from 'react'
import { useHistory } from 'react-router-dom'

export const useTracking = (trackingId) => {
  const { listen } = useHistory()

  useEffect(() => {
    const unlisten = listen((location) => {
      // if you pasted the google snippet on your index.html
      // you've declared this function in the global
      if (!window.gtag) return

      window.gtag('config', trackingId, { page_path: location.pathname })
    })

    // remember, hooks that add listeners
    // should have cleanup to remove them
    return unlisten
  }, [trackingId, listen])
}

आपको अपने ऐप में एक बार इस हुक का उपयोग करना चाहिए , कहीं शीर्ष के पास लेकिन फिर भी एक राउटर के अंदर। मैं इसे App.jsइस तरह दिखता है:

App.js

import * as React from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'

import Home from './Home/Home'
import About from './About/About'
// this is the file above
import { useTracking } from './useTracking'

export const App = () => {
  useTracking('UA-USE-YOURS-HERE')

  return (
    <Switch>
      <Route path="/about">
        <About />
      </Route>
      <Route path="/">
        <Home />
      </Route>
    </Switch>
  )
}

// I find it handy to have a named export of the App
// and then the default export which wraps it with
// all the providers I need.
// Mostly for testing purposes, but in this case,
// it allows us to use the hook above,
// since you may only use it when inside a Router
export default () => (
  <BrowserRouter>
    <App />
  </BrowserRouter>
)

1

मुझे इस सवाल का सामना करना पड़ा क्योंकि मैं रिएक्ट सिंगल पेज ऐप में एक नई स्क्रीन पर नेविगेट करने के बाद "स्क्रीन" के शीर्ष पर ChromeVox स्क्रीन रीडर को फोकस करने का प्रयास कर रहा था। मूल रूप से यह अनुकरण करने की कोशिश की जा रही है कि क्या होगा यदि इस पेज को एक नए सर्वर द्वारा प्रदान किए गए वेब पेज के लिंक के द्वारा लोड किया गया था।

यह समाधान किसी भी श्रोताओं की आवश्यकता नहीं है, इसे इस्तेमाल करता है withRouter()और componentDidUpdate()जीवन चक्र विधि जब एक नया url पथ पर नेविगेट करके वांछित तत्व पर ChromeVox ध्यान केंद्रित करने के लिए एक क्लिक गति प्रदान करने के।


कार्यान्वयन

मैंने एक "स्क्रीन" घटक बनाया जो प्रतिक्रिया-राउटर स्विच टैग के चारों ओर लिपटा हुआ है जिसमें सभी ऐप्स स्क्रीन हैं।

<Screen>
  <Switch>
    ... add <Route> for each screen here...
  </Switch>
</Screen>

Screen.tsx अंग

नोट: यह घटक React + TypeScript का उपयोग करता है

import React from 'react'
import { RouteComponentProps, withRouter } from 'react-router'

class Screen extends React.Component<RouteComponentProps> {
  public screen = React.createRef<HTMLDivElement>()
  public componentDidUpdate = (prevProps: RouteComponentProps) => {
    if (this.props.location.pathname !== prevProps.location.pathname) {
      // Hack: setTimeout delays click until end of current
      // event loop to ensure new screen has mounted.
      window.setTimeout(() => {
        this.screen.current!.click()
      }, 0)
    }
  }
  public render() {
    return <div ref={this.screen}>{this.props.children}</div>
  }
}

export default withRouter(Screen)

मैंने focus()इसके बजाय का उपयोग करने की कोशिश की थी click(), लेकिन वर्तमान में जो कुछ भी पढ़ रहा है, उसे पढ़ना बंद कर दें और जहां मैं इसे शुरू करने के लिए कहूं, उसे पढ़ना बंद कर दें।

उन्नत नोट: इस समाधान में, नेविगेशन <nav>जो कि स्क्रीन घटक के अंदर है और <main>सामग्री के बाद प्रदान किया गया है, का mainउपयोग करते हुए सीएसएस के ऊपर स्थित है order: -1;। इसलिए छद्म कोड में:

<Screen style={{ display: 'flex' }}>
  <main>
  <nav style={{ order: -1 }}>
<Screen>

यदि आपके पास इस समाधान के बारे में कोई विचार, टिप्पणी या सुझाव हैं, तो कृपया एक टिप्पणी जोड़ें।


1

राउटर R5 V5

यदि आप पथनाम को एक स्ट्रिंग ('/' या 'उपयोगकर्ता') के रूप में चाहते हैं, तो आप निम्नलिखित का उपयोग कर सकते हैं:

  // React Hooks: React Router DOM
  let history = useHistory();
  const location = useLocation();
  const pathName = location.pathname;

0
import React from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Sidebar from './Sidebar';
import Chat from './Chat';

<Router>
    <Sidebar />
        <Switch>
            <Route path="/rooms/:roomId" component={Chat}>
            </Route>
        </Switch>
</Router>

import { useHistory } from 'react-router-dom';
function SidebarChat(props) {
    **const history = useHistory();**
    var openChat = function (id) {
        **//To navigate**
        history.push("/rooms/" + id);
    }
}

**//To Detect the navigation change or param change**
import { useParams } from 'react-router-dom';
function Chat(props) {
    var { roomId } = useParams();
    var roomId = props.match.params.roomId;

    useEffect(() => {
       //Detect the paramter change
    }, [roomId])

    useEffect(() => {
       //Detect the location/url change
    }, [location])
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.