Incorrect use of useParams() when destructuring

Loading

Proper Usage of useParams() in React Router

When working with dynamic route parameters in React Router, the useParams() hook is essential but often misused during destructuring. Here’s how to handle it correctly.

The Core Problem

Common mistakes when using useParams():

// ❌ Incorrect - assumes param always exists
const { id } = useParams(); 

// ❌ Incorrect - type unsafe
const params = useParams();
const productId = params.productId; // Could be undefined

Correct Implementation

1. Basic Usage with Optional Chaining

function ProductPage() {
  const params = useParams();
  const productId = params.productId; // string | undefined

  if (!productId) {
    return <div>Product not found</div>;
  }

  return <ProductDetails id={productId} />;
}

2. Type-Safe Approach (TypeScript)

interface ProductParams {
  productId: string;
}

function ProductPage() {
  const { productId } = useParams<ProductParams>();

  if (!productId) {
    return <div>Product not found</div>;
  }

  return <ProductDetails id={productId} />;
}

Common Mistakes and Solutions

❌ Assuming Parameters Always Exist

function UserProfile() {
  const { userId } = useParams(); // ❌ userId could be undefined

  useEffect(() => {
    fetchUser(userId); // ❌ Potential runtime error
  }, [userId]);

  return <div>User Profile</div>;
}

Fix: Add existence check

function UserProfile() {
  const { userId } = useParams();

  useEffect(() => {
    if (!userId) return;
    fetchUser(userId); // ✅ Safe
  }, [userId]);

  if (!userId) return <div>User not found</div>;

  return <div>User Profile</div>;
}

❌ Not Handling Optional Parameters

// Route: "/posts/:postId?"
function PostPage() {
  const { postId } = useParams(); // ❌ May be undefined

  return <div>{postId ? `Post ${postId}` : 'All Posts'}</div>;
}

Fix: Explicitly handle optional case

function PostPage() {
  const params = useParams();
  const isSinglePost = !!params.postId;

  return (
    <div>
      {isSinglePost ? (
        <SinglePost id={params.postId!} />
      ) : (
        <AllPosts />
      )}
    </div>
  );
}

Advanced Patterns

1. Parameter Validation

function isValidId(id: string | undefined): id is string {
  return !!id && /^[a-z0-9-]+$/.test(id);
}

function ProductPage() {
  const { productId } = useParams();

  if (!isValidId(productId)) {
    return <NotFound />;
  }

  return <ProductDetails id={productId} />;
}

2. Multiple Parameters

interface CategoryParams {
  category: string;
  subcategory?: string;
}

function CategoryPage() {
  const { category, subcategory } = useParams<CategoryParams>();

  return (
    <div>
      <h1>{category}</h1>
      {subcategory && <h2>{subcategory}</h2>}
    </div>
  );
}

3. With Query Parameters

function SearchPage() {
  const { query } = useParams(); // From route: "/search/:query"
  const [searchParams] = useSearchParams(); // From URL: "?sort=asc"
  const sortOrder = searchParams.get('sort');

  return <SearchResults query={query || ''} sort={sortOrder} />;
}

TypeScript Best Practices

1. Explicit Parameter Types

type RouteParams = {
  projectId: string;
  taskId?: string;
};

function TaskPage() {
  const { projectId, taskId } = useParams<RouteParams>();
  // projectId is string | undefined
  // taskId is string | undefined
}

2. Non-Null Assertion (When Appropriate)

// Only use when route MUST have the parameter
function UserDetail() {
  const { userId } = useParams() as { userId: string };
  // Or
  const { userId } = useParams<{ userId: string }>();

  return <UserProfile id={userId} />;
}

Debugging Tips

  1. Log parameters to verify:
   const params = useParams();
   console.log('Route params:', params);
  1. Check your route definitions match the URL structure:
   <Route path="/products/:productId" element={<ProductPage />} />
  1. Use React DevTools to inspect matched routes
  2. Verify parameter names match between route and usage

Key Takeaways

  1. Never assume parameters exist – always check
  2. Use TypeScript for type safety with parameters
  3. Handle optional parameters explicitly
  4. Validate parameters when they affect data fetching
  5. Combine with other hooks like useSearchParams when needed

Proper use of useParams() ensures your dynamic routes work reliably and helps prevent runtime errors from undefined parameters. Always consider the edge cases where parameters might be missing or invalid.

Leave a Reply

Your email address will not be published. Required fields are marked *