Search code examples
typescriptreact-router-domdataloader

React router V6.5 : how to strongly type data loaders?


using React-Router V6 and I tried to strongly type the dataloader functions that are using params and also the useLoaderData hook.

So far I have to do the following that is ugly :

A- For useLoaderData, need to force the returnType :

const profil = useLoaderData() as TProfil;

I guess it would be cleaner to create a generic hook like export declare function useLoaderData<T>(): T; instead of export declare function useLoaderData(): unknown;

B- for the dataloader, what is the type of the params received ? Had to force to any, but this is uggly. How to strongly type this and declare somewhere that params is made of "id" which comes from the parameter name in the Route definition ?

const careerDetailDataLoader = async ({ params }: any): Promise<TProfil> => {
  const { id } = params;
  const res = await fetch(`http://localhost:4000/careers/${id}`);

  const data: TProfil = await res.json();
  return data;
};

<Route path=":id" element={<CareerDetailsPage />} loader={careerDetailDataLoader} />

Solution

  • First question

    I guess it would be cleaner to create a generic hook like export declare function useLoaderData(): T; instead of export declare function useLoaderData(): unknown;

    Any solution using generics only hides a necessary type cast using as so the generic version gives a false sense of type safety.

    Remix has exactly the generic useLoaderData<T>() that you suggest. At the remix github repo, there is a detailed explanation of why they deprecated the generic version of the function and why the useLoaderData() function which they ported to react-router v6 does not use generics. (The suggested helper function in the other answer has the same problem as the deprecated function in remix.)

    Second question

    for the dataloader, what is the type of the params received ?

    In react-router, there is ActionFunctionArgs which leads to Params being defined (in @remix-run/router/dist/utils.d.ts/Params) like this:

    export type Params<Key extends string = string> = {
        readonly [key in Key]: string | undefined;
    };
    

    However, you might want to check out https://stackoverflow.com/a/76579584/5882233 for a similar question (and potentially a cleaner solution) about the argument types of the loader and action functions of react-router v6.