ฉันมีปุ่มสองสามปุ่มที่ทำหน้าที่เป็นเส้นทาง ทุกครั้งที่เปลี่ยนเส้นทางฉันต้องการให้แน่ใจว่าปุ่มที่ใช้งานอยู่เปลี่ยนไป
มีวิธีฟังการเปลี่ยนแปลงเส้นทางใน react router v4 หรือไม่?
ฉันมีปุ่มสองสามปุ่มที่ทำหน้าที่เป็นเส้นทาง ทุกครั้งที่เปลี่ยนเส้นทางฉันต้องการให้แน่ใจว่าปุ่มที่ใช้งานอยู่เปลี่ยนไป
มีวิธีฟังการเปลี่ยนแปลงเส้นทางใน react router v4 หรือไม่?
คำตอบ:
ฉันใช้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>
}
}
หวังว่าจะช่วยได้
withRouter
You should not use <Route> or withRouter() outside a <Router>
ฉันไม่เห็น<Router/>
ส่วนประกอบใด ๆในโค้ดด้านบน แล้วมันทำงานอย่างไร?
<Switch>
ส่วนประกอบกำลังทำหน้าที่เป็นเราเตอร์โดยพฤตินัย ระบบจะแสดงเฉพาะ<Route>
รายการแรกที่มีเส้นทางที่ตรงกัน ไม่จำเป็นต้องมี<Router/>
ส่วนประกอบใด ๆในสถานการณ์นี้
หากต้องการขยายด้านบนคุณจะต้องไปที่วัตถุประวัติ หากคุณกำลังใช้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
.
คุณควรใช้history 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}`)
})
history.push
history.listen
ดูการใช้ฐาน URLตัวอย่างเช่นในประวัติศาสตร์เอกสาร v4 เนื่องจากนั่นhistory
เป็นกระดาษห่อหุ้มของhistory
ออบเจ็กต์ดั้งเดิมของเบราว์เซอร์จึงไม่ทำงานเหมือนกับเนทีฟ
const unlisten = history.listen(myListener); unlisten();
withRouter
, history.listen
และuseEffect
(React เบ็ด) ทำงานอย่างมากด้วยกัน
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
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>
}
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]);
}
ด้วยตะขอ
react-router-dom
แทนreact-router
ด้วยตะขอ:
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>
ส่วนประกอบ
import { useHistory } from 'react-router-dom';
const Scroll = () => {
const history = useHistory();
useEffect(() => {
window.scrollTo(0, 0);
}, [history.location.pathname]);
return null;
}
ด้วย react Hooks ฉันใช้ 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])
สำหรับส่วนประกอบที่ใช้งานได้ลอง useEffect กับ props.location
import React, {useEffect} from 'react';
const SampleComponent = (props) => {
useEffect(() => {
console.log(props.location);
}, [props.location]);
}
export default SampleComponent;
ในบางกรณีคุณอาจใช้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
วิธีการนี้อาจทำให้เกิดข้อผิดพลาด 'ความลึกของการอัปเดตสูงสุดเกิน'
ด้วย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);
ฉันเพิ่งจัดการกับปัญหานี้ดังนั้นฉันจะเพิ่มวิธีแก้ปัญหาของฉันเป็นส่วนเสริมสำหรับคำตอบอื่น ๆ ที่ได้รับ
ปัญหาคือมันuseEffect
ไม่ได้ผลจริงอย่างที่คุณต้องการเนื่องจากการโทรจะถูกทริกเกอร์หลังจากการเรนเดอร์ครั้งแรกเท่านั้นจึงเกิดความล่าช้าที่ไม่ต้องการ
หากคุณใช้ตัวจัดการสถานะบางอย่างเช่น redux โอกาสที่คุณจะได้รับการกะพริบบนหน้าจอเนื่องจากสถานะค้างอยู่ในร้านค้า
สิ่งที่คุณต้องการจริงๆคือใช้useLayoutEffect
เนื่องจากสิ่งนี้ถูกทริกเกอร์ทันที
ดังนั้นฉันจึงเขียนฟังก์ชันยูทิลิตี้ขนาดเล็กที่ฉันใส่ไว้ในไดเร็กทอรีเดียวกับเราเตอร์ของฉัน:
export const callApis = (fn, path) => {
useLayoutEffect(() => {
fn();
}, [path]);
};
ซึ่งฉันเรียกจากภายในส่วนประกอบ HOC ดังนี้:
callApis(() => getTopicById({topicId}), path);
path
เป็นเสาที่ได้รับการผ่านในวัตถุเมื่อใช้match
withRouter
ฉันไม่ชอบฟัง / ไม่ฟังประวัติด้วยตนเองจริงๆ นั่นเป็นเพียง imo