Как правильно набрать Redux Connect Call? - PullRequest
0 голосов
/ 12 января 2019

Я пытаюсь использовать хранилище состояний Redux в сочетании с TypeScript. Я пытаюсь использовать официальную типизацию Redux и хочу сделать весь вызов по методу connect (который соединяет mapStatetoProps и mapDispatchToProps с компонентом) типа safe.

Я обычно вижу подходы, когда методы mapStatetoProps и mapDispatchToProps просто набираются пользователем и возвращают часть компонентов компонента, например:

function mapStateToProps(state: IStateStore, ownProps: Partial<IComponentProps>)
  : Partial<IProjectEditorProps> {}
function mapDispatchToProps (dispatch: Dispatch, ownProps: Partial<IComponentProps>)
  : Partial<IProjectEditorProps> {}

Это типизировано и работает, но не совсем безопасно, потому что можно создать экземпляр компонента, который пропускает реквизиты, так как использование интерфейса Partial допускает неполные определения. Однако здесь требуется интерфейс Partial, потому что вы можете определить некоторые реквизиты в mapStateToProps и некоторые в mapDispatchToProps, а не все в одной функции. Вот почему я хочу избежать этого стиля.

То, что я сейчас пытаюсь использовать, - это непосредственное встраивание функций в вызов connect и ввод вызова connect с помощью универсальной типизации, предоставляемой redux:

connect<IComponentProps, any, any, IStateStore>(
  (state, ownProps) => ({
    /* some props supplied by redux state */
  }),
  dispatch => ({
    /* some more props supplied by dispatch calls */
  })
)(Component);

Однако это также приводит к ошибке, которую встроенные mapStatetoProps и mapDispatchToProps призывают не определять все Реквизиты каждый , поскольку оба требуют только их подмножества, но вместе определяя все реквизиты.

Как правильно набрать вызов connect, чтобы вызовы mapStatetoProps и mapDispatchToProps действительно были безопасными при наборе типов, и проверяет, что объединенные значения, определенные обоими методами, предоставляют все необходимые реквизиты без одного из методов, необходимых для определения всех реквизит сразу? Возможно ли это как-то с моим подходом?

Ответы [ 2 ]

0 голосов
/ 12 января 2019

Одним из решений является разделение свойств вашего компонента на свойства state, dispatch и, возможно, own:

import React from "react";
import { connect } from "react-redux";

import { deleteItem } from "./action";
import { getItemById } from "./selectors";

interface StateProps {
  title: string;
}

interface DispatchProps {
  onDelete: () => any;
}

interface OwnProps {
  id: string;
}

export type SampleItemProps = StateProps & DispatchProps & OwnProps;

export const SampleItem: React.SFC<SampleItemProps> = props => (
  <div>
    <div>{props.title}</div>
    <button onClick={props.onDelete}>Delete</button>
  </div>
);

// You can either use an explicit mapStateToProps...
const mapStateToProps = (state: RootState, ownProps: OwnProps) : StateProps => ({
  title: getItemById(state, ownProps.id)
});

// Ommitted mapDispatchToProps...

// ... and infer the types from connects arguments ...
export default connect(mapStateToProps, mapDispatchToProps)(SampleItem);

// ... or explicitly type connect and "inline" map*To*.
export default connect<StateProps, DispatchProps, OwnProps, RootState>(
  (state, ownProps) => ({
    title: getItemById(state, ownProps.id)
  }),
  (dispatch, ownProps) => ({
    onDelete: () => dispatch(deleteItem(ownProps.id))
  })
)(SampleItem);
0 голосов
/ 12 января 2019

Вариант 1: Разделить IComponentProps

Самый простой способ сделать это, вероятно, просто определить отдельные интерфейсы для ваших "реквизитов, полученных из состояния", ваших "собственных реквизитов" и ваших "реквизитов диспетчеризации", а затем использовать тип пересечения , чтобы объединить их вместе для IComponentProps

import * as React from 'react';
import { connect, Dispatch } from 'react-redux'
import { IStateStore } from '@src/reducers';


interface IComponentOwnProps {
  foo: string;
}

interface IComponentStoreProps {
  bar: string;
}

interface IComponentDispatchProps {
  fooAction: () => void;
}

type IComponentProps = IComponentOwnProps & IComponentStoreProps & IComponentDispatchProps

class IComponent extends React.Component<IComponentProps, never> {
  public render() {
    return (
      <div>
        foo: {this.props.foo}
        bar: {this.props.bar}
        <button onClick={this.props.fooAction}>Do Foo</button>
      </div>
    );
  }
}

export default connect<IComponentStoreProps, IComponentDispatchProps, IComponentOwnProps, IStateStore>(
  (state, ownProps): IComponentStoreProps => {
    return {
      bar: state.bar + ownProps.foo
    };
  },
  (dispatch: Dispatch<IStateStore>): IComponentDispatchProps => (
    {
      fooAction: () => dispatch({type:'FOO_ACTION'})
    }
  )
)(IComponent);

Мы можем установить общие параметры функции соединения следующим образом: <TStateProps, TDispatchProps, TOwnProps, State>

Вариант 2: пусть ваши функции определяют ваш интерфейс Props

Другой вариант, который я видел в дикой природе, - это использование ReturnType сопоставленного типа , чтобы позволить вашим mapX2Props функциям фактически определить, что они вносят в IComponentProps.

type IComponentProps = IComponentOwnProps & IComponentStoreProps & IComponentDispatchProps;

interface IComponentOwnProps {
  foo: string;
}

type IComponentStoreProps = ReturnType<typeof mapStateToProps>;
type IComponentDispatchProps = ReturnType<typeof mapDispatchToProps>;

class IComponent extends React.Component<IComponentProps, never> {
  //...
}


function mapStateToProps(state: IStateStore, ownProps: IComponentOwnProps) {
  return {
    bar: state.bar + ownProps.foo,
  };
}

function mapDispatchToProps(dispatch: Dispatch<IStateStore>) {
  return {
    fooAction: () => dispatch({ type: 'FOO_ACTION' })
  };
}

export default connect<IComponentStoreProps, IComponentDispatchProps, IComponentOwnProps, IStateStore>(
  mapStateToProps,
  mapDispatchToProps
)(IComponent);

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

Я всегда держался в стороне от ReturnType, упростите, потому что чувствует себя задом наперед, чтобы ваша реализация определяла ваш программный интерфейс "контрактами" (IMO). Почти становится слишком просто изменить ваш IComponentProps так, как вы не собирались.

Однако, поскольку все здесь довольно автономно, это, вероятно, приемлемый вариант использования.

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