React Router v6 的 withRouter 包装器正确键入,没有任何

8 typescript reactjs react-router react-router-dom

为了不重建从类组件到功能组件的所有内容,我需要使用 React Router文档中的包装器:

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;
}

Run Code Online (Sandbox Code Playgroud)

但是我需要更正Typescript 中Component的类型props。(任何快速测试都表明它有效)。但我需要使用哪些正确的类型?

这是我尝试过的,给出Component类型Function,但仍然不确定道具,因为我想避免使用任何..

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;

Run Code Online (Sandbox Code Playgroud)

编辑:

所以我发现我正在寻找的是React.ComponentType,它是 ComponentClass 和 ComponentFunction 的联合。

但是当我使用Component: React.ComponentType时,TS 在返回时在我的路由器上抛出错误 -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'.
Run Code Online (Sandbox Code Playgroud)

所以这会引发一个新的错误,我该如何解决这个问题?

ghy*_*ybs -1

确实有一些关于输入常见问题解答建议withRouter实现的博客,例如https://whereisthemouse.com/how-to-use-withrouter-hoc-in-react-router-v6-with-typescript

但看起来它使用的是以前版本的常见问题解答,其中location,paramsnavigateprops 直接内联为 props 键,而新的常见问题解答将它们收集在一个router字典中。这完全取决于您的类组件期望如何接收这些道具。

既然您说忽略类型问题(通常与any)在您的项目中有效,那么它就符合这种形式。因此,让我们调整一下输入:

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;
Run Code Online (Sandbox Code Playgroud)

我们来尝试一下:

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) */}
</>
Run Code Online (Sandbox Code Playgroud)

看起来不错!

游乐场链接