Passing State Data Between Components Using useNavigate and useLocation in React Router Dom v6

Nov 27, 2025 · Programming · 16 views · 7.8

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:

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:

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.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.