Jawaban:
Saya gunakan withRouter
untuk mendapatkan location
prop. Ketika komponen diperbarui karena rute baru, saya memeriksa apakah nilainya berubah:
@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>
}
}
Semoga ini bisa membantu
withRouter
, tetapi saya mendapatkan kesalahan You should not use <Route> or withRouter() outside a <Router>
. Saya tidak melihat <Router/>
komponen dalam kode di atas. Jadi bagaimana cara kerjanya?
<Switch>
komponen tersebut bertindak sebagai router de-facto. Hanya <Route>
entri pertama yang memiliki jalur yang cocok, akan diberikan. Tidak perlu <Router/>
komponen apa pun dalam skenario ini
Untuk memperluas di atas, Anda harus mendapatkan objek sejarah. Jika Anda menggunakan BrowserRouter
, Anda dapat mengimpor withRouter
dan membungkus komponen Anda dengan komponen orde tinggi (HoC) agar memiliki akses melalui alat peraga ke properti dan fungsi objek sejarah.
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);
Satu-satunya hal yang perlu diperhatikan adalah bahwa denganouter dan sebagian besar cara lain untuk mengakses history
tampaknya mencemari alat peraga ketika mereka de-struktur objek ke dalamnya.
withRoutes
untuk withRouter
.
Anda harus menggunakan riwayat v4 lib.
Contoh dari sana
history.listen((location, action) => {
console.log(`The current URL is ${location.pathname}${location.search}${location.hash}`)
console.log(`The last navigation action was ${action}`)
})
history.push
memicu history.listen
. Lihat contoh Menggunakan URL Pangkalan di riwayat v4 docs . Karena itu history
sebenarnya pembungkus history
objek asli browser, itu tidak berperilaku persis seperti yang asli.
const unlisten = history.listen(myListener); unlisten();
withRouter
,, history.listen
dan useEffect
(React Hooks) bekerja sangat baik bersama-sama:
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)
Panggilan balik pendengar akan diaktifkan setiap kali rute diubah, dan pengembaliannya history.listen
adalah penangan shutdown yang bekerja dengan baik useEffect
.
v5.1 memperkenalkan kait berguna useLocation
https://reacttraining.com/blog/react-router-v5-1/#uselokasi
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>
}
Cannot read property 'location' of undefined at useLocation
. Anda perlu memastikan bahwa panggilan useLocation () tidak berada di komponen yang sama dengan yang menempatkan router ke pohon: lihat di sini
Dengan kait:
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)
Impor dan render sebagai <DebugHistory>
komponen
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]);
}
dengan kait
Dengan Hooks bereaksi, saya menggunakan 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])
Dalam beberapa kasus, Anda mungkin menggunakan render
atribut alih-alih component
, dengan cara ini:
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>
}
}
Perhatikan bahwa jika Anda mengubah status dalam onRouteChange
metode, ini dapat menyebabkan kesalahan 'Kedalaman pembaruan maksimum terlampaui'.
Dengan useEffect
kait itu mungkin untuk mendeteksi perubahan rute tanpa menambahkan pendengar.
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);
Saya hanya berurusan dengan masalah ini, jadi saya akan menambahkan solusi saya sebagai suplemen pada jawaban lain yang diberikan.
Masalahnya di sini adalah bahwa useEffect
tidak benar-benar berfungsi seperti yang Anda inginkan, karena panggilan hanya akan dipicu setelah render pertama sehingga ada penundaan yang tidak diinginkan.
Jika Anda menggunakan beberapa pengelola negara seperti redux, kemungkinan Anda akan mendapatkan layar berkedip karena keadaan yang tersisa di toko.
Apa yang benar-benar Anda inginkan adalah menggunakan useLayoutEffect
karena ini akan segera dipicu.
Jadi saya menulis fungsi utilitas kecil yang saya taruh di direktori yang sama dengan router saya:
export const callApis = (fn, path) => {
useLayoutEffect(() => {
fn();
}, [path]);
};
Yang saya panggil dari dalam komponen HOC seperti ini:
callApis(() => getTopicById({topicId}), path);
path
adalah penyangga yang dilewatkan di match
objek saat menggunakan withRouter
.
Saya tidak terlalu suka mendengarkan / tidak mendengarkan secara manual tentang sejarah. Itu hanya imo.