Каков тип функционального составного компонента с маршрутизатором?

#reactjs #typescript #react-router

#reactjs #typescript #react-маршрутизатор

Вопрос:

Я пытаюсь использовать функциональные составные компоненты с помощью react-router. Каким будет тип Login.button в интерфейсе LoginComposition?

 import React, { FC } from 'react'; 
import { Button } from '../../styles/login'; 
import { RouteComponentProps, withRouter } from 'react-router-dom';

interface LoginComposition {    
    Button: <INSERT TYPE HERE>; 
}

const Login: FC amp; LoginComposition = ({ children }) => {    
    return <div>{children}</div>; 
};

const LoginButton: FC<RouteComponentProps> = ({ children, history }) => {
    return <Button onClick={() => history.push('foo')}>{children}</Button>; 
};

Login.Button = withRouter(LoginButton);

export default Login;
  

Комментарии:

1. Вы можете использовать function Login вместо const Login и просто назначить Login.Button , и в этом случае TypeScript автоматически определит правильный тип.

Ответ №1:

Сначала я отвечу на вопрос напрямую, затем я объясню способ избежать сумасшествия Typescript. Для чего-то подобного вам нужно изменить порядок определения типов и функций. Вы могли бы извлечь типы react-router , которые генерирует пакет, и перепроектировать тип withRouter() , но я проверил пакет, и он не симпатичный, и он не будет устойчивым, если тип изменится. Что вам нужно, так это немедленно обернуть LoginButton withRouter(...) или создать вторую переменную для ее хранения. Примеры здесь:

 const LoginButton = withRouter(({ children, history }) => {
    return <Button onClick={() => history.push('foo')}>{children}</Button>; 
});

// OR

const LoginButton: FC<RouteComponentProps> = ({ children, history }) => {
    return <Button onClick={() => history.push('foo')}>{children}</Button>; 
};

const WrappedLoginButton = withRouter(LoginButton);

  

Затем последним шагом будет поместить Login компонент и интерфейс под LoginButton (или WrappedLoginButton ) и использовать typeof для извлечения из него ввода. Вся настройка будет выглядеть следующим образом (с использованием WrappedLoginButton )

 
import React, { FC } from 'react'; 
import { Button } from '../../styles/login'; 
import { RouteComponentProps, withRouter } from 'react-router-dom';

const LoginButton: FC<RouteComponentProps> = ({ children, history }) => {
    return <Button onClick={() => history.push('foo')}>{children}</Button>; 
};

const WrappedLoginButton = withRouter(LoginButton);

interface LoginComposition {    
    Button: typeof WrappedLoginButton; 
}

const Login: FC amp; LoginComposition = ({ children }) => {    
    return <div>{children}</div>; 
};

Login.Button = WrappedLoginButton;

export default Login;
  

Чтобы упростить код, есть ли причина, по которой вы не можете просто использовать useHistory() хук? Особенно с Typescript, компоненты более высокого порядка могут быть довольно привередливыми. Если вы используете хук следующим образом:

 const LoginButton: FC<RouteComponentProps> = ({ children }) => {
    const history = useHistory();
    return <Button onClick={() => history.push('foo')}>{children}</Button>; 
};
  

Это уменьшит потребность в синтаксисе withRouter, что означает, что типы могут быть сведены к следующему:

 import React, { FC } from 'react'; 
import { Button } from '../../styles/login'; 
import { RouteComponentProps, withRouter } from 'react-router-dom';

const LoginButton: FC<RouteComponentProps> = ({ children }) => {
    const history = useHistory();
    return <Button onClick={() => history.push('foo')}>{children}</Button>; 
};

interface LoginComposition {    
    Button: typeof LoginButton; 
}

const Login: FC amp; LoginComposition = ({ children }) => {    
    return <div>{children}</div>; 
};


Login.Button = withRouter(LoginButton);

export default Login;