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.