Подтвердить что ты не робот

Response-router-dom с TypeScript

Я пытаюсь использовать реактивный маршрутизатор с TypeScript. Тем не менее, у меня есть определенные проблемы с использованием функции roRouter. В последней строке я получаю довольно странную ошибку:

Argument of type 'ComponentClass<{}>' is not assignable to parameter of type 'StatelessComponent<RouteComponentProps<any>> | ComponentClass<RouteComponentProps<any>>'.
  Type 'ComponentClass<{}>' is not assignable to type 'ComponentClass<RouteComponentProps<any>>'.
    Type '{}' is not assignable to type 'RouteComponentProps<any>'.
      Property 'match' is missing in type '{}’

Код выглядит так:

import * as React from 'react';
import { connect } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router-dom';

interface HomeProps extends RouteComponentProps<any> {
}

interface HomeState { }

class Home extends React.Component<HomeProps, HomeState> {
  constructor(props: HomeProps) {
    super(props);
  }
  public render(): JSX.Element {
    return (<span>Home</span>);
  }
}

const connectModule = connect(
  (state) => ({
    // Map state to props
  }),
  {
    // Map dispatch to props
  })(Home);

export default withRouter(connectModule);
4b9b3361

Ответ 1

Я использую другой подход, чтобы исправить это. Я всегда разделяю различные свойства (маршрутизатор, регулярный и диспетчерский), поэтому я определяю следующие интерфейсы для моего компонента:

interface HomeRouterProps {
  title: string;   // This one is coming from the router
}

interface HomeProps extends RouteComponentProps<HomeRouterProps> {
  // Add your regular properties here
}

interface HomeDispatchProps {
  // Add your dispatcher properties here
}

Теперь вы можете либо создать новый тип, который объединяет все свойства в одном типе, но я всегда совмещаю типы во время определения компонента (я не добавляю состояние здесь, но если вам нужно, просто продолжайте). Определение компонента выглядит следующим образом:

class Home extends React.Component<HomeProps & HomeDispatchProps> {
  constructor(props: HomeProps & HomeDispatchProps) {
    super(props);
  }

  public render() {
    return (<span>{this.props.match.params.title}</span>);
  }
}

Теперь нам нужно подключить компонент к состоянию через контейнер. Это выглядит так:

function mapStateToProps(state, ownProps: HomeProps): HomeProps => {
  // Map state to props (add the properties after the spread)
  return { ...ownProps };
}

function mapDispatchToProps(dispatch): HomeDispatchProps {
  // Map dispatch to props
  return {};
}

export default connect(mapStateToProps, mapDispatchToProps)(Hello);

Этот метод позволяет полностью типизированное соединение, поэтому компонент и контейнер полностью набраны, и его можно повторно реорганизовать. Единственное, что небезопасно для рефакторинга, - это параметр маршрута, который сопоставляется с интерфейсом HomeRouterProps.

Ответ 2

Я думаю, что это проблема компиляции типов typescript, но я нашел обходное решение:

interface HomeProps extends RouteComponentProps<any>, React.Props<any> {
}

Ответ 3

Это проблема типографской типизации. Поскольку withRouter() будет предоставлять реквизиты маршрутизации, которые вам нужны во время выполнения, вы хотите сказать потребителям, что им нужно только указать другие реквизиты. В этом случае вы можете просто использовать простой ComponentClass:

export default withRouter(connectModule) as React.ComponentClass<{}>;

Или, если у вас были другие реквизиты (определенные в интерфейсе OwnProps), которые вы хотели бы пройти, вы могли бы сделать это:

export default withRouter(connectModule) as React.ComponentClass<OwnProps>;

Существует немного больше обсуждения здесь

Ответ 4

Похоже, что у вас есть правильное использование, чтобы применить реквизит соответствия, истории и местоположения к вашему компоненту. Я бы посмотрел в вашем каталоге node_modules, чтобы узнать, какие версии react-router и react-router-dom у вас есть, а также модули @types.

У меня есть тот же самый код, что и вы, и мой работает со следующими версиями:

{
  "@types/react-router-dom": "^4.0.4",
  "react-router-dom": "^4.1.1",
}

Ответ 5

После просмотра описаний RouteComponentProps я обнаружил интерфейс RouteComponentProps поэтому теперь я моделирую свои контейнеры так

type RouteParams = {
    teamId: string; // must be type string since route params
}

interface Props extends RouteComponentProps<RouteParams>, React.Props<RouteParams> { }

type State = {
    players: Array<Player>;
}

export class PlayersContainer extends React.Component<Props, State>{} 

теперь в классе компонентов можно получить доступ к реквизитам маршрута следующим образом: let teamid = this.props.match.params.teamId;

Ответ 6

Вы можете попробовать следующее:

export default class AppContainer extends React.Component<{}, {}> {
  render() {
    return (
      <BrowserRouter>
          <div>
             <Route path="/" exact={true} component={Login as any} />
             <Route path="/home" component={Home as any} />
          </div>
      </BrowserRouter>
    );
  }
}

Ответ 7

+1 для ответа Рамона. Здесь вы можете получить полный охват. Чтобы добавить бит - я добавил вызов withRouter.

interface FooProps extends RouteComponentProps<Foo> {
  foo: string;
}

const Foo = ({ history, foo }: FooProps) => <span/>;

const RoutedFoo = withRouter(Foo);

мои зависимости:

"@types/react-router-dom": "^4.3.0",
"typescript": "^2.9.2",

Ответ 8

Способ, которым я делаю это:

interface IOwnProps {
  style?: CSSProperties;
}

interface INavProps {
  item?: string;
}

type IProps = IOwnProps & RouteComponentProps<INavProps>;

export default class MapScreen extends PureComponent<IProps> {
  ...
}