React TypeScript HoC - 将组件作为道具传递

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/49274143/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 05:16:57  来源:igfitidea点击:

React TypeScript HoC - passing Component as the prop

javascriptreactjstypescriptjsx

提问by 0leg

Following this tutorial: https://reacttraining.com/react-router/web/example/auth-workflow.

遵循本教程:https: //reacttraining.com/react-router/web/example/auth-workflow

Trying to reproduce the code:

试图重现代码:

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route
    {...rest}
    render={props =>
      fakeAuth.isAuthenticated ? (
        <Component {...props} />
      ) : (
        <Redirect
          to={{
            pathname: "/login",
            state: { from: props.location }
          }}
        />
      )
    }
  />
);

In TypeScript:

在打字稿中:

import * as React from 'react';
import { Route, RouterProps } from 'react-router';

interface Props extends RouterProps {
  component: React.Component;
}

const PrivateRoute = ({ component: Component, ...rest }: Props) => {
  return (
    <Route
      {...rest}
      render={(props) => <Component {...props} />}
    />
  );
};

export default PrivateRoute;

But it would always fail. Tried different variations. The one I've posted the most recent one. Getting:

但它总是会失败。尝试了不同的变体。我最近贴的那个。获得:

enter image description here

在此处输入图片说明

It seems to me that I have to pass Generic for the Component type, but I don't know how.

在我看来,我必须为 Component 类型传递 Generic,但我不知道如何。

EDIT:

编辑:

The closest solution so far:

迄今为止最接近的解决方案:

interface Props extends RouteProps {
  component: () => any;
}

const PrivateRoute = ({ component: Component, ...rest }: Props) => {
  return (
    <Route
      {...rest}
      render={(props) => <Component {...props} />}
    />
  );
};

And then:

接着:

<PrivateRoute component={Foo} path="/foo" />

回答by Titian Cernicova-Dragomir

You want to pass a component constructor, not a component instance:

你想传递一个组件构造函数,而不是一个组件实例:

import * as React from 'react';
import { Route, RouteProps } from 'react-router';

interface Props extends RouteProps {
    component: React.ComponentType;
}

const PrivateRoute = ({ component: Component, ...rest }: Props) => {
    return (
        <Route
            {...rest}
            render={(props) => <Component {...props} />}
        />
    );
};

export default PrivateRoute;

class Foo extends React.Component {

}
let r = <PrivateRoute component={Foo} path="/foo" />

Edit

编辑

A more complete solution should be generic and use RoutePropsinstead RouterProps:

更完整的解决方案应该是通用的,RouteProps而是使用RouterProps

import * as React from 'react';
import { Route, RouteProps } from 'react-router';

type Props<P> =  RouteProps & P & {
    component: React.ComponentType<P>;
}

const PrivateRoute = function <P>(p: Props<P>) {
    // We can't use destructuring syntax, because : "Rest types may only be created from object types", so we do it manually.
    let rest = omit(p, "component");
    let Component = p.component;
    return (
        <Route
            {...rest}
            render={(props: P) => <p.component {...props} />}
        />
    );
};

// Helpers
type Diff<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T];  
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>; 
function omit<T, TKey extends keyof T>(value:T, ... toRemove: TKey[]): Omit<T, TKey>{
    var result = Object.assign({}, value);
    for(let key of toRemove){
        delete result[key];
    }
    return result;
}


export default PrivateRoute;

class Foo extends React.Component<{ prop: number }>{

}
let r = <PrivateRoute component={Foo} path="/foo" prop={10} />

回答by 0leg

After a few hours and some investigation, here is the solution that fits my requirements:

经过几个小时和一些调查,这是符合我要求的解决方案:

import * as React from 'react';
import { Route, RouteComponentProps, RouteProps } from 'react-router';

const PrivateRoute: React.SFC<RouteProps> =
  ({ component: Component, ...rest }) => {
    if (!Component) {
      return null;
    }
    return (
      <Route
        {...rest}
        render={(props: RouteComponentProps<{}>) => <Component {...props} />}
      />
    );
  };

export default PrivateRoute;


  • No any;
  • No extra complexity;
  • Composition pattern retained;
  • 没有any
  • 没有额外的复杂性;
  • 构图图案保留;