1. Use the `react-router` library (taking `react-router-dom` as an example)
1. Historical object monitoring
1.1 Principle
`react-router` internally uses the `history` object to manage routing history. You can listen for routing changes by accessing the `history` object. In a class-based component, the history object can be obtained through the component's `props`; in a functional component, the useHistory` hook function can be obtained.
1.2 Example (class-based components)
import React from "react"; import { withRouter } from "react-router-dom"; class MyComponent extends { componentDidMount() { ((location, action) => { ("Route changed, new location:", location); ("Route Change Action:", action); }); } render() { return <div>This is a component</div>; } } export default withRouter(MyComponent);
Here, in the `componentDidMount` lifecycle method, add a route change listener through ``. Whenever the route changes, a new location (`location`) and route changes actions (`action`, such as `PUSH`, `REPLACE`, etc.) will be printed out.
1.3 Example (functional component)
import React from "react"; import { useHistory } from "react-router-dom"; function MyComponent() { const history = useHistory(); (() => { const unlisten = ((location, action) => { ("Route changed, new location:", location); ("Route Change Action:", action); }); return () => { unlisten(); }; }, [history]); return <div>This is a functional component</div>; } export default MyComponent;
In a functional component, use the `useHistory` hook to get the `history` object and add the listener in the `useEffect` hook. Also, a cleanup function is returned to remove the listener when the component is unloaded.
2. `useLocation` hook listening (recommended for functional components)
2.1 Principle
`useLocation` is a hook function provided by `react-router-dom`, which returns the current `location` object. By comparing the changes in the `location` object before and after, it can be detected whether the route has changed.
2.2 Example
import React from "react"; import { useLocation } from "react-router-dom"; function MyComponent() { const location = useLocation(); (() => { ("Current routing location:", location); }, [location]); return <div>This is a functional component</div>; } export default MyComponent;
Here, the `useEffect` hook depends on the `location` object. Whenever the `location` changes (i.e., the routing changes), the callback function in `useEffect` will be executed and the current routing location is printed out.
3. Custom event listening (not relying on the internal mechanism of `react-router`)
3.1 Principle
In top-level components such as the App component, route changes are detected by listening to the `hashchange` (for hash routing) or `popstate` (for HTML5 history routing) events through the `addEventListener` method of the `window object. This method is more in-depth and requires you to deal with more details yourself, such as distinguishing different types of routing and handling event bubbles.
3.2 Example (taking hash routing as an example)
import React from "react"; function App() { (() => { const handleHashChange = () => { ("The hash route has changed, the current hash:", ); }; ("hashchange", handleHashChange); return () => { ("hashchange", handleHashChange); }; }, []); return <div>{/* Routing related components and content */}</div>; } export default App;
Avoid common listening misunderstandings: performance optimization and user experience
Although there are many ways to implement when monitoring routing changes in React projects, if used improperly, it is easy to fall into some performance and user experience misunderstandings. Here are common mistakes and optimization suggestions to help you get the best performance and user experience in your project. These suggestions also apply to general performance optimization.
Performance Trap One: Over-rendering
When listening for routing changes, developers often perform a large number of logical operations directly in the component's useEffect or componentDidUpdate. Each time the routing changes, the entire component is re-rendered, which may cause a significant decline in the performance of the page.
Question example:
In the following example, useEffect performs a large number of operations, including data fetching and DOM updates, each time the routing changes, which can cause performance issues.
import React, { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; const MyComponent = () => { const location = useLocation(); const [data, setData] = useState(null); useEffect(() => { // Execute every time the route changes ('Route changed:', ); // Simulated data acquisition fetch(`/api/data?path=${}`) .then(response => ()) .then(data => setData(data)); // Simulate other side effects = `Current path: ${}`; }, [location]); // Dependencies are the entire location object return <div>{data ? `Data: ${data}` : 'Loading...'}</div>; }; export default MyComponent;
Optimization example:
With conditional rendering or relying on granular listening, ensure that components will re-render only if they are really needed. For example, make sureuseEffect
The dependency array is accurate and avoids unnecessary duplicate execution.
import React, { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; const MyComponent = () => { const location = useLocation(); const [data, setData] = useState(null); useEffect(() => { // Update data only when path changes if ( === '/specific-path') { fetch(`/api/data?path=${}`) .then(response => ()) .then(data => setData(data)); } }, []); // Rely on path changes only useEffect(() => { // Update document title only when path changes = `Current path: ${}`; }, []); // Rely on path changes only return <div>{data ? `Data: ${data}` : 'Loading...'}</div>; }; export default MyComponent;
We can also use this process. Use , to avoid unnecessary re-rendering of child components, or to useCallback cache function to ensure that the listening logic is re-executed only when the dependencies change.
Performance Trap 2: Unnecessary Monitoring
For simple routing changes scenarios, developers may use complex listening logic or frequently call APIs. This not only wastes resources, but may also lead to slower overall application response.
Question example:
In the following example, the listening logic may be too complex and is performed in a global component, resulting in unnecessary resource consumption.
import React, { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; const GlobalListener = () => { const location = useLocation(); useEffect(() => { ('Route changed globally:', ); // Assume that global monitoring and operation are required fetch(`/api/global-data`) .then(response => ()) .then(data => (data)); }, [location]); return null; }; export default GlobalListener;
Optimization example:
If the routing changes do not affect all components, they should only listen where they are needed. Concentrate listening logic in related components to avoid global listening.
import React, { useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; const SpecificPage = () => { const location = useLocation(); const [data, setData] = useState(null); useEffect(() => { if ( === '/specific-page') { // Execute logic only in specific pages fetch(`/api/specific-data`) .then(response => ()) .then(data => setData(data)); } }, []); // Execute only in specific pages return <div>{data ? `Data: ${data}` : 'Loading...'}</div>; }; export default SpecificPage;
Performance Trap Three: Too Many Side Effects
When listening for routing changes, developers often perform multiple side effects when changes occur, such as page jumps, data loading, etc. This way of stacking side effects can cause page loading to slow down, especially when routing switches quickly, users may feel noticeable stutter.
Question example:
In the following example, multiple side effects are performed simultaneously when routing changes, which can cause page stuttering.
import React, { useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; const MyComponent = () => { const location = useLocation(); const navigate = useNavigate(); const [data, setData] = useState(null); useEffect(() => { // Execute multiple side effects fetch(`/api/data?path=${}`) .then(response => ()) .then(data => setData(data)); = `Current path: ${}`; navigate('/another-path'); // Navigate to another path }, [location]); return <div>{data ? `Data: ${data}` : 'Loading...'}</div>; }; export default MyComponent;
Optimization example:
Split side effects into small, independent tasks and use lazy loading or delayed execution to reduce performance burden.
import React, { useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; const MyComponent = () => { const location = useLocation(); const navigate = useNavigate(); const [data, setData] = useState(null); useEffect(() => { const fetchData = async () => { // Delayed execution of data acquisition if ( === '/specific-path') { const response = await fetch(`/api/data?path=${}`); const result = await (); setData(result); } }; // Execute delayed data acquisition fetchData(); // Update the title only when the path changes = `Current path: ${}`; // Delay navigation to another path const timer = setTimeout(() => { navigate('/another-path'); }, 500); return () => clearTimeout(timer); // Clean the timer }, [location]); return <div>{data ? `Data: ${data}` : 'Loading...'}</div>; }; export default MyComponent;
in conclusion
Routing monitoring is a key link that cannot be ignored in React projects. Through reasonable listening methods, you can make your application perform better in navigation, data loading, user interaction, etc. At the same time, we must also pay attention to changes in routing, and ignore changes in routing may lead to degradation in user experience and unnecessary performance overhead.
The above is the detailed content of React's solution for routing change monitoring. For more information about React's routing change monitoring, please pay attention to my other related articles!