TypeScript 与 Redux 容器的斗争 [英] TypeScript struggles with Redux containers

查看:41
本文介绍了TypeScript 与 Redux 容器的斗争的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在弄清楚如何正确输入 Redux 容器.

I'm having some trouble figuring out how to properly type Redux containers.

考虑一个可能如下所示的简单展示组件:

Consider a simple presentational component that might look like this:

interface MyProps {
  name: string;
  selected: boolean;
  onSelect: (name: string) => void;
}
class MyComponent extends React.Component<MyProps, {}> { }

从这个组件的角度来看,所有的道具都是必需的.

From the perspective of this component all props are required.

现在我想写一个容器,将所有这些 props 拉出状态:

Now I want to write a container that pulls all these props out of state:

function mapStateToProps(state: MyState) {
  return {
    name: state.my.name,
    selected: state.my.selected
  };
}

function mapDispatchToProps(dispatch: IDispatch) {
  return {
    onSelect(name: string) {
      dispatch(mySelectAction(name));
    }
  };
}

const MyContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(MyComponent);

这是可行的,但有一个很大的输入问题:映射函数(mapStateToPropsmapDispatchToProps)没有保护它们提供正确的数据来实现 我的道具.这很容易出错、打字错误和重构不佳.

This works, but there's a big typing problem: the mapping functions (mapStateToProps and mapDispatchToProps) have no protection that they are providing the right data to fulfill MyProps. This is prone to error, typos and poor refactoring.

我可以让映射函数返回类型 MyProps:

I could make the mapping functions return type MyProps:

function mapStateToProps(state: MyState): MyProps { }

function mapDispatchToProps(dispatch: IDispatch): MyProps { }

然而,除非我将所有 MyProp 道具设为可选,否则这不起作用,这样每个映射函数只能返回它们关心的部分.我不想让 props 成为可选的,因为它们对于展示组件来说不是可选的.

However this doesn't work unless I make all MyProp props optional, so that each mapping function can return only the portion they care about. I don't want to make the props optional, because they aren't optional to the presentational component.

另一种选择是将每个地图函数的 props 拆分,并为组件 props 组合它们:

Another option is to split up the props for each map function and combine them for the component props:

// MyComponent
interface MyStateProps {
  name: string;
  selected: boolean;
}

interface MyDispatchProps {
  onSelect: (name: string) => void;
}

type MyProps = MyStateProps & MyDispatchProps;

class MyComponent extends React.Component<MyProps, {}> { }

// MyContainer
function mapStateToProps(state: MyState): MyStateProps { }

function mapDispatchToProps(dispatch: IDispatch): MyDispatchProps { }

好的,这让我更接近我想要的东西,但它有点嘈杂,它让我围绕容器的形状编写我的展示组件道具界面,这是我不喜欢的.

Ok, that's getting me closer to what I want, but its kind of noisy and its making me write my presentational component prop interface around the shape of a container, which I don't like.

现在出现了第二个问题.如果我想将容器放在另一个组件中怎么办:

And now a second problem arises. What if I want to put the container inside another component:

<MyContainer />

这会导致nameselectedonSelect 都丢失的编译错误......但这是故意的,因为容器正在连接到Redux 并提供这些.所以这促使我重新将所有组件 props 设为可选,但我不喜欢那样,因为它们并不是真正可选的.

This gives compile errors that name, selected, and onSelect are all missing... but that's intentional because the container is connecting to Redux and providing those. So this pushes me back to making all component props optional, but I don't like that because they aren't really optional.

MyContainer 有一些自己想要传入的 props 时,事情会变得更糟:

Things get worse when MyContainer has some of its own props that it wants to be passed in:

<MyContainer section="somethng" />

现在我要做的是让 section 成为 MyContainer 的必需道具,但不是 MyComponent 的道具,以及 nameselectedonSelectMyComponent 的必需道具,但是 MyContainer<的可选道具或根本不是道具/代码>.我完全不知该如何表达.

Now what I'm trying to do is have section a required prop of MyContainer but not a prop of MyComponent, and name, selected, and onSelect are required props of MyComponent but optional or not props at all of MyContainer. I'm totally at a loss how to express this.

对此的任何指导将不胜感激!

Any guidence on this would be appreciated!

推荐答案

你的上一个例子是正确的.您还需要定义一个MyOwnProps 接口,并键入connect 函数.

You're on the right track with your last example. What you also need to define is a MyOwnProps interface, and type the connect function.

使用这些类型:https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/react-redux/react-redux.d.ts,你可以这样做

With these typings: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/react-redux/react-redux.d.ts, you can do something like this

interface MyProps {
  section: string;
  name: string;
  selected: boolean;
  onSelect: (name: string) => void;
}

interface MyStateProps {
  name: string;
  selected: boolean;
}

interface MyDispatchProps {
  onSelect: (name: string) => void;
}

interface MyOwnProps {
  section: string;
}

class MyComponent extends React.Component<MyProps, {}> { }

function mapStateToProps(state: MyState): MyStateProps { }

function mapDispatchToProps(dispatch: IDispatch): MyDispatchProps { }

const MyContainer = connect<MyStateProps, MyDispatchProps, MyOwnProps>(
  mapStateToProps,
  mapDispatchToProps
)(MyComponent);

这还允许您在 mapStateToPropsmapDispatchToProps 中键入 ownProps,例如

This also lets you type ownProps in mapStateToProps and mapDispatchToProps, e.g.

function mapStateToProps(state: MyState, ownProps: MyOwnProps): MyStateProps

只要定义了 dispatch、state 和 ownProps 类型,就不需要为 MyProps 类型定义交集类型.这样,您可以将 MyProps 保留在自己的位置,并让容器或在没有容器的情况下使用组件的地方根据需要应用道具.我想这取决于您和您的用例 - 如果您定义 MyProps = MyStateProps &MyDispatchProps &MyOwnProps,它绑定到一个特定的容器,灵活性较差(但不那么冗长).

You don't need to define an intersection type for MyProps type as long as you define dispatch, state, and ownProps types. That way you can keep MyProps in its own place and let containers, or places where the component is used without containers, apply the props as they need to. I guess this is up to you and your use case - if you define MyProps = MyStateProps & MyDispatchProps & MyOwnProps, it's tied to one specific container, which is less flexible (but less verbose).

作为一个解决方案,它非常冗长,但我认为没有任何方法可以告诉 TypeScript 所需道具的不同部分将在不同的地方组装,而 connect 将把它们绑在一起.

As a solution, it is pretty verbose, but I don't think there's any way of getting around telling TypeScript that the different pieces of required props will be assembled in different places, and connect will tie them together.

另外,为了它的价值,为了简单起见,我通常使用可选的道具,所以我没有太多关于使用这种方法的经验可以分享.

Also, for what it's worth, I have typically gone with optional props, for simplicity's sake, so I don't have much in the way of experience to share on using this approach.

这篇关于TypeScript 与 Redux 容器的斗争的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆