TypeScript 与 Redux 容器的斗争 [英] TypeScript struggles with Redux containers
问题描述
我在弄清楚如何正确输入 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);
这是可行的,但有一个很大的输入问题:映射函数(mapStateToProps
和 mapDispatchToProps
)没有保护它们提供正确的数据来实现 我的道具
.这很容易出错、打字错误和重构不佳.
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 />
这会导致name
、selected
和onSelect
都丢失的编译错误......但这是故意的,因为容器正在连接到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
的道具,以及 name
、selected
和 onSelect
是 MyComponent
的必需道具,但是 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);
这还允许您在 mapStateToProps
和 mapDispatchToProps
中键入 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屋!