Вывод сопоставленных реквизитов при использовании TypeScript и React-Redux - PullRequest
1 голос
/ 30 октября 2019

Я нашел способ получить безопасность типов при использовании mapStateToProps из react-redux: как задокументировано вы можете определить интерфейс и параметризовать React.Component<T> с вашим интерфейсом.

Однакокогда я определяю mapStateToProps, я уже определяю функцию, в которой можно определить типы свойств результирующего объекта. Например,

function mapStateToProps(state: MyState) {
    return {
        counter: state.counter
    };
}

Здесь можно сделать вывод, что реквизит counter соответствует типу state.counter. Но мне все еще нужно иметь стандартный код, подобный следующему:

interface AppProps {
    counter: number;
}


class App extends React.Component<AppProps> { ... }

export default connect(mapStateToProps)(App);

Так что вопрос в том, есть ли способ структурировать код, чтобы я мог избежать написания типа counter дважды? Или чтобы избежать параметризации типа React.Component - даже если бы я мог получить реквизиты компонента, выведенные из явно намекаемого типа результата функции mapStateToProps, это было бы предпочтительнее. Мне интересно, является ли приведенное выше дублирование действительно нормальным способом написания типизированных компонентов с использованием React-Redux.

Ответы [ 3 ]

3 голосов
/ 30 октября 2019

Да. Существует аккуратная техника для вывода типа комбинированных реквизитов, которые connect передадут вашему компоненту на основе mapState и mapDispatch.

. Существует новый тип ConnectedProps<T>, который доступен в @types/react-redux@7.1.2. Вы можете использовать его следующим образом:

function mapStateToProps(state: MyState) {
    return {
        counter: state.counter
    };
}

const mapDispatch = {increment};

// Do the first half of the `connect()` call separately, 
// before declaring the component
const connector = connect(mapState, mapDispatch);

// Extract "the type of the props passed down by connect"
type PropsFromRedux = ConnectedProps<typeof connector>
// should be: {counter: number, increment: () => {type: "INCREMENT"}}, etc

// define combined props
type MyComponentProps = PropsFromRedux & PropsFromParent;

// Declare the component with the right props type
class MyComponent extends React.Component<MyComponentProps> {}

// Finish the connect call
export default connector(MyComponent)

Обратите внимание, что это правильно выводит тип создателей групповых действий, включенных в mapDispatch, если это объект, тогда как typeof mapDispatch - нет.

Мы добавим это к официальным документам React-Redux в качестве рекомендуемого подхода в ближайшее время.

Подробнее:

3 голосов
/ 30 октября 2019

Я так не думаю. Вы можете сделать вашу настройку более краткой, используя перехватчики Redux: https://react -redux.js.org / next / api / hooks

    // Your function component . You don't need to connect it
    const App: React.FC = () => {
      const counter = useSelector<number>((state: MyState) => state.counter);
      const dispatch = useDispatch(); // for dispatching actions
    };

Редактировать: Вы можете, если вы просто используететот же тип MyState. Но я не думаю, что вы этого захотите.

2 голосов
/ 30 октября 2019

Я бы набрал сопоставленные реквизиты диспетчеризации и компоненты отдельно, а затем объединил выведенный тип сопоставленного состояния в функцию реквизитов. Смотрите ниже для быстрого примера. Возможно, будет более элегантное решение, но, надеюсь, оно поможет вам выбрать правильный путь.

import * as React from "react";
import { Action } from "redux";
import { connect } from "react-redux";

// Lives in some lib file
type AppState = {
  counter: number;
};

type MappedState = {
  computedValue: number;
};
type MappedDispatch = {
  doSomethingCool: () => Action;
};
type ComponentProps = {
  someProp: string;
};

const mapStateToProps = (state: AppState) => ({
  computedValue: state.counter
});

const mapDispatchToProps: MappedDispatch = {
  doSomethingCool: () => {
    return {
      type: "DO_SOMETHING_COOL"
    };
  }
};

type Props = ReturnType<typeof mapStateToProps> &
  MappedDispatch &
  ComponentProps;

class DumbComponent extends React.Component<Props> {
  render() {
    return (
      <div>
        <h1>{this.props.someProp}</h1>
        <div>{this.props.computedValue}</div>
        <button onClick={() => this.props.doSomethingCool()}>Click me</button>
      </div>
    );
  }
}

const SmartComponent = connect(
  mapStateToProps,
  mapDispatchToProps
)(DumbComponent);

export default SmartComponent;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...