React TypeScript: Correct type for useLocation() from react-router-dom

Viewed 34955

I'm struggling to find the right type for this situation. This is a simplified version of redirecting after login. The following produces a compiler error:

Property 'from' does not exist on type '{} | { from: { pathname: string; }; }'.

Adding as any to the use of location.state fixes the compiler error but it's ugly and the linter complains.

import React from "react";
import { useLocation } from "react-router-dom";

const AuthLayer: React.FC = (props) => {
  const location = useLocation();

  const { from } = location.state || { from: { pathname: "/" } };

  return <p></p>;
};

export default AuthLayer;
7 Answers

You can create a particular type or interface to describe your location state and then use it when calling a useLocation hook:

import React from "react";
import { useLocation } from "react-router-dom";

interface LocationState {
  from: {
    pathname: string;
  };
}

const AuthLayer: React.FC = (props) => {
  const location = useLocation<LocationState>();

  const { from } = location.state || { from: { pathname: "/" } };

  return <p></p>;
};

export default AuthLayer;

Type Assertion would work here.

import React from "react";
import { useLocation } from "react-router-dom";

type LocationState = {
  from: {
    path: string;
  }
}

const AuthLayer: React.FC = (props) => {
  const location = useLocation();

  const { from } = location.state as LocationState;

  return <p></p>;
};

export default AuthLayer;

Also, remember to define the type, as per your requirements. For example, you might be using navigate(state.from).

For this Define the type as -

type LocationState = {
  from : string;
}

You can use Location from 'history'.

import React from "react";
import { Location } from "history";
import { useLocation } from "react-router-dom";


const AuthLayer: React.FC = (props) => {
  const location = useLocation<Location>();

  const { from } = location.state || { from: { pathname: "/" } };

  return <p></p>;
};

export default AuthLayer;

This will be a little type-safe since it will return undefined if from is not in the object instead of throwing an error.

import React from "react";
import { useLocation } from "react-router-dom";

type LocationState = {
  from: {
    path: string;
  }
}

const AuthLayer: React.FC = (props) => {
  const location = useLocation();
  
  // 
  const from = (location.state as LocationState)?.from;

  return <p></p>;
};

export default AuthLayer;
export interface LocationParams {
  pathname: string;
  state: your_state_data_type;
  search: string;
  hash: string;
  key: string;
}

//...
const location = useLocation<LocationParams>()

or create a generic that u can use any where

// types.ts
export interface LocationParams<Data> {
  pathname: string;
  state: Data;
  search: string;
  hash: string;
  key: string;
}


// App.tsx
import { LocationParams } from './types.ts'

// ...
const location = useLocation<LocationParams<your_data_here>>()

In react-router-dom version 6, it can be done like that

const locationState = useLocation().state as {id: string}; 

const id = locationState.id;

If you send only one thing, What about using useParams instead of useLocation?

function routeDetails(id: string) {
    history.push({
      pathname: `/details/${pathname}`,
    });
  }
const { id } = useParams<{ id: string }>();

You can use useParams simply. It has key 'id' that is from history pathname.

Related