Issue
In order to not rebuild everything from class components to functional components I need to use the wrapper from the React Router Documentation:
import {
useLocation,
useNavigate,
useParams,
} from "react-router-dom";
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
However I need to correct types for Component
and props
in Typescript. (A quick test with any shows it works). But what are the correct types that I need to use?
This is what I tried, give Component
the Function
type but still not sure with props as I want to avoid to use any..
import { useLocation, useNavigate, useParams } from "react-router-dom";
type IComponentWithRouterProp = {
[x: string]: any;
};
function withRouter(Component: Function) {
function ComponentWithRouterProp(props: IComponentWithRouterProp) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return <Component {...props} router={{ location, navigate, params }} />;
}
return ComponentWithRouterProp;
}
export default withRouter;
EDIT:
So I found out that what I am looking for is React.ComponentType, which is a union of ComponentClass and ComponentFunction.
But when I use Component: React.ComponentType
then TS throws an error at my router in the return - router={{ location, navigate, params }}
Type '{ router: { location: Location; navigate: NavigateFunction; params: Readonly<Params<string>>; }; }' is not assignable to type 'IntrinsicAttributes'.
Property 'router' does not exist on type 'IntrinsicAttributes'.
So this throws a new error, how can I solve this?
Solution
There are indeed some blogs about typing the FAQ proposed withRouter
implementation, e.g. https://whereisthemouse.com/how-to-use-withrouter-hoc-in-react-router-v6-with-typescript
But it looks like it was using a previous version of the FAQ, where location
, params
and navigate
props were directly inlined as props keys, whereas the new FAQ gathers them in a single router
dictionary. It all depends on how your class components expect to receive these props.
Since you say that ignoring type issues (typically with any
) works in your project, then it complies with this form. So let's adapt the typings:
import { useLocation, useNavigate, useParams } from "react-router-dom";
export interface WithRouterProps {
location: ReturnType<typeof useLocation>;
params: Record<string, string>;
navigate: ReturnType<typeof useNavigate>;
}
function withRouter<CProps extends { router: WithRouterProps }>(Component: React.ComponentType<CProps>) {
function ComponentWithRouterProp(props: Omit<CProps, "router">) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return <Component {...(props as CProps)} router={{ location, navigate, params }} />;
}
return ComponentWithRouterProp;
}
export default withRouter;
Let's try it:
class Welcome extends React.Component<{ name: string; router: WithRouterProps }> {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
// Bare component expects name and router props
<>
<Welcome name="Foo" /> {/* Error: Property 'router' is missing in type '{ name: string; }' but required in type 'Readonly<{ name: string; router: WithRouterProps; }>'.(2769)*/}
<Welcome name="Foo" router={({} as WithRouterProps)} />
</>
const WelcomeWithRouter = withRouter(Welcome);
// withRouter'ed component expects only name, router will be automatically provided
<>
<WelcomeWithRouter name="Foo" />
<WelcomeWithRouter name="Foo" router={({} as WithRouterProps)} /> {/* Error: Property 'router' does not exist on type 'IntrinsicAttributes & Omit<{ name: string; router: WithRouterProps; }, "router">'.(2322) */}
</>
Looks good!
Answered By - ghybs
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.