Tôi có một vài nút hoạt động như các tuyến đường. Mỗi khi tuyến đường được thay đổi, tôi muốn đảm bảo nút đang hoạt động thay đổi.
Có cách nào để lắng nghe những thay đổi tuyến đường trong phản ứng của bộ định tuyến v4 không?
Tôi có một vài nút hoạt động như các tuyến đường. Mỗi khi tuyến đường được thay đổi, tôi muốn đảm bảo nút đang hoạt động thay đổi.
Có cách nào để lắng nghe những thay đổi tuyến đường trong phản ứng của bộ định tuyến v4 không?
Câu trả lời:
Tôi sử dụng withRouter
để có được chỗ dựa location
. Khi thành phần được cập nhật vì một tuyến mới, tôi kiểm tra xem giá trị có thay đổi không:
@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>
}
}
Hy vọng nó giúp
withRouter
, nhưng tôi đang gặp lỗi You should not use <Route> or withRouter() outside a <Router>
. Tôi không thấy bất kỳ <Router/>
thành phần nào trong đoạn mã trên. Vậy nó hoạt động như thế nào?
<Switch>
thành phần này đóng vai trò là bộ định tuyến thực tế. Chỉ <Route>
mục đầu tiên có đường dẫn phù hợp, sẽ được hiển thị. Không cần bất kỳ <Router/>
thành phần nào trong kịch bản này
Để mở rộng ở trên, bạn sẽ cần lấy tại đối tượng lịch sử. Nếu bạn đang sử dụng BrowserRouter
, bạn có thể nhập withRouter
và bọc thành phần của mình bằng thành phần bậc cao hơn (HoC) để có quyền truy cập thông qua các đạo cụ đến các thuộc tính và chức năng của đối tượng lịch sử.
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);
Điều duy nhất cần biết là vớiRouter và hầu hết các cách khác để truy cập vào history
dường như làm ô nhiễm đạo cụ khi chúng cấu trúc lại vật thể vào đó.
withRoutes
sang withRouter
.
Bạn nên sử dụng lib v4 lịch sử .
Ví dụ từ đó
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
không kích hoạt history.listen
. Xem ví dụ Sử dụng URL cơ sở trong tài liệu v4 lịch sử . Bởi vì đó history
thực sự là một trình bao bọc của history
đối tượng gốc của trình duyệt, nên nó không hoạt động chính xác như đối tượng gốc.
const unlisten = history.listen(myListener); unlisten();
withRouter
, history.listen
và useEffect
(Móc phản ứng) hoạt động khá độc đáo với nhau:
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)
Cuộc gọi lại người nghe sẽ kích hoạt bất cứ khi nào một tuyến đường được thay đổi, và sự trở lại history.listen
là một trình xử lý tắt máy hoạt động tốt useEffect
.
v5.1 giới thiệu móc hữu ích 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>
}
Cannot read property 'location' of undefined at useLocation
. Bạn cần đảm bảo rằng lệnh gọiLocation () không nằm trong cùng một thành phần đặt bộ định tuyến vào cây: xem tại đây
Với móc:
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)
Nhập và kết xuất dưới dạng <DebugHistory>
thành phần
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]);
}
với móc
Với phản ứng Hook, tôi đang sử dụng 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])
Trong một số trường hợp, bạn có thể sử dụng render
thuộc tính thay vì component
, theo cách này:
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>
}
}
Lưu ý rằng nếu bạn thay đổi trạng thái trong onRouteChange
phương thức, điều này có thể gây ra lỗi 'Độ sâu cập nhật tối đa vượt quá'.
Với useEffect
hook, có thể phát hiện các thay đổi tuyến đường mà không cần thêm người nghe.
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);
Tôi chỉ giải quyết vấn đề này, vì vậy tôi sẽ thêm giải pháp của mình dưới dạng bổ sung cho các câu trả lời khác được đưa ra.
Vấn đề ở đây là useEffect
nó không thực sự hoạt động như bạn mong muốn, vì cuộc gọi chỉ được kích hoạt sau lần kết xuất đầu tiên nên có độ trễ không mong muốn.
Nếu bạn sử dụng một số trình quản lý trạng thái như redux, rất có thể bạn sẽ bị nhấp nháy trên màn hình vì trạng thái kéo dài trong cửa hàng.
Những gì bạn thực sự muốn là sử dụng useLayoutEffect
vì điều này được kích hoạt ngay lập tức.
Vì vậy, tôi đã viết một chức năng tiện ích nhỏ mà tôi đặt trong cùng thư mục với bộ định tuyến của mình:
export const callApis = (fn, path) => {
useLayoutEffect(() => {
fn();
}, [path]);
};
Mà tôi gọi từ bên trong thành phần HOC như thế này:
callApis(() => getTopicById({topicId}), path);
path
là chỗ dựa được truyền vào match
đối tượng khi sử dụngwithRouter
.
Tôi không thực sự ủng hộ việc nghe / không nghe theo cách thủ công trong lịch sử. Đó chỉ là imo.