Keywords: React Router Dom | useNavigate | useLocation
Abstract: This article provides an in-depth exploration of how to pass state data between components in React Router Dom v6 using the useNavigate hook and retrieve it with useLocation. Through practical code examples, it demonstrates the complete workflow of transferring selected row data from Material-UI table components to report pages, addressing common state passing issues while offering alternative solutions for class components using higher-order components.
Introduction
In modern single-page application development, data passing between components is a common requirement. React Router Dom v6 introduces a new navigation pattern where the useNavigate hook replaces the older useHistory, providing a more concise API for handling route navigation and state passing.
Problem Analysis
In React Router Dom v6, developers frequently encounter challenges with correctly passing state data to target components. Common issues include:
- Proper usage of the
navigatemethod - Methods for retrieving state data in target components
- Compatibility issues with class components that cannot use hooks
Core Solution
Using useNavigate to Pass State
In the sending component, when navigating via the useNavigate hook, any data can be passed through the state property of the second parameter:
import { useNavigate } from 'react-router-dom';
function LeadTable(props) {
let navigate = useNavigate();
function redirectToReport(rowData) {
navigate('/app/report', { state: rowData });
}
// Rest of component implementation
}Here, rowData can be any JavaScript object, including complex data structures like arrays and nested objects.
Using useLocation to Receive State
In the target component, access the passed state data through the useLocation hook:
import { useLocation } from 'react-router-dom';
function ReportPage() {
const location = useLocation();
const { state } = location;
console.log(state); // Access the passed rowData here
return (
<div>
<div className="Top3">
<h3>Top 3 Leads</h3>
<ReportTop3 leads={state || []} />
</div>
</div>
);
}Complete Implementation Example
Table Component Implementation
Complete implementation integrating Material-UI table components:
export default function LeadTable(props) {
let navigate = useNavigate();
const [leads, setLeads] = useState([]);
const [loading, setLoading] = useState(true);
async function fetchUrl(url) {
const response = await fetch(url);
const json = await response.json();
setLeads(json[0]);
setLoading(false);
}
useEffect(() => {
fetchUrl("http://localhost:5000/api/leads");
}, []);
function redirectToReport(rowData) {
navigate('/app/report', { state: rowData });
}
return (
<div>
<TableRows leads={leads} redirect={redirectToReport} />
</div>
);
}
function TableRows(props) {
return (
<MaterialTable
title="Leads"
columns={[
// Column definitions
]}
data={props.leads}
options={{
selection: true,
filtering: true,
sorting: true
}}
actions={[{
position: "toolbarOnSelect",
tooltip: 'Generate a report based on selected leads.',
icon: 'addchart',
onClick: (event, rowData) => {
console.log("Row Data: ", rowData);
props.redirect(rowData);
}
}]}
/>
);
}Report Page Component
Optimized report page component with proper state data handling:
export default function ReportPage() {
const location = useLocation();
const { state } = location;
// Add data validation and default value handling
const reportData = state || [];
return (
<div>
<div className="Top3">
<h3>Top 3 Leads</h3>
<ReportTop3 leads={reportData} />
</div>
</div>
);
}Class Component Compatibility Solution
For class components that cannot use hooks, achieve the same functionality through higher-order components:
import { useLocation } from 'react-router-dom';
const withRouter = WrappedComponent => props => {
const location = useLocation();
return (
<WrappedComponent
{...props}
location={location}
/>
);
};
class ReportPage extends Component {
render() {
const { location } = this.props;
console.log(location.state);
return (
<div>
<div className="Top3">
<h3>Top 3 Leads</h3>
<ReportTop3 leads={location.state || []} />
</div>
</div>
);
}
}
export default withRouter(ReportPage);Best Practices
Data Validation
In real-world projects, validate passed state data:
function ReportPage() {
const location = useLocation();
const { state } = location;
// Validate data format
if (!Array.isArray(state)) {
console.warn('Invalid data format received');
return <div>Invalid data</div>;
}
return (
// Component content
);
}Error Handling
Implement appropriate error handling mechanisms:
function redirectToReport(rowData) {
try {
navigate('/app/report', { state: rowData });
} catch (error) {
console.error('Navigation failed:', error);
// Handle navigation failure scenarios
}
}Performance Optimization Considerations
When passing large amounts of data, consider these optimization strategies:
- Pass only necessary data, avoid transferring entire datasets
- For large objects, consider using data identifiers instead of complete data
- Implement data lazy loading in target components
Conclusion
React Router Dom v6's useNavigate and useLocation hooks provide a concise yet powerful mechanism for state passing between components. By correctly utilizing these APIs, developers can easily implement data transfer between components while maintaining good code structure and maintainability. For class components, the higher-order component pattern ensures compatibility, facilitating smooth project migration.