typescript + redux:在父组件中排除 redux props
Posted
技术标签:
【中文标题】typescript + redux:在父组件中排除 redux props【英文标题】:typescript + redux: exclude redux props in parent component 【发布时间】:2017-09-16 01:41:32 【问题描述】:我正在为我当前的 web 应用程序使用 redux 和 typescript。
定义通过@connect
接收redux-actions 的组件的props 以及来自父级的props 的最佳实践是什么?
示例:
// mychild.tsx
export namespace MyChildComponent
export interface IProps
propertyFromParent: string;
propertyFromRedux: string; // !!!! -> This is the problem
actionsPropertyFromRedux: typeof MyReduxActions; // !!!! -> And this
@connect(mapStateToProps, mapDispatchToProps)
export class MyChildComponent extends React.Component <MyChildComponent.IProps, any>
... react stuff
function mapStateToProps(state: RootState)
return
propertyFromRedux: state.propertyFromRedux
;
function mapDispatchToProps(dispatch)
return
actionsPropertyFromRedux: bindActionCreators(MyReduxActions as any, dispatch)
;
// myparent.tsx
export class MyParentComponent extends React.Component <MyParentComponent.IProps, any>
... react stuff
render()
// typescript complains, because I am not passing `propertyFromRedux`!
return <div><MyChildComponent propertyFromParent="yay" /></div>;
在我看来,我有 2 个解决方案。
只需通过我的整个应用程序传递操作和状态。但这意味着我的整个应用程序会被重新渲染,即使只需要更改一些小的子组件。还是在我的***组件中监听所有商店更改的 redux 方式?然后我必须在shouldComponentUpdate
中为非平面对象的道具编写大量逻辑。
将IProps
中的参数设置为MyChildComponent
可选,如下所示:
-
// mychild.tsx
export namespace MyChildComponent
export interface IProps
propertyFromParent: string;
propertyFromRedux?: typeof MyAction; // This is the problem
还有其他方法吗?以上两种方式在我看来都太乱了。
【问题讨论】:
【参考方案1】:您需要拆分道具 - 您需要 DispatchProps
、StateProps
和 OwnProps
类型。您还必须使用 TypeScript 的泛型和 connect
DispatchProps
是您的动作创建者。
StateProps
是您的状态道具(废话) - 这些来自 mapStateToProps
- 该函数的返回类型应与此类型匹配。
OwnProps
是您的组件接受(并且可能是预期)的道具。可选道具应在界面中标记为可选。
我这样做的方式(没有装饰器,但我确信它适用于这里)是
interface ComponentDispatchProps
doSomeAction: typeof someAction;
interface ComponentStateProps
somethingFromState: any;
interface ComponentOwnProps
somethingWhichIsRequiredInProps: any;
somethingWhichIsNotRequiredInProps?: any;
// not necessary to combine them into another type, but it cleans up the next line
type ComponentProps = ComponentStateProps & ComponentDispatchProps & ComponentOwnProps;
class Component extends React.Component<ComponentProps, > ...
function mapStateToProps(state, props)
return somethingFromState ;
export default connect<ComponentStateProps, ComponentDispatchProps, ComponentOwnProps>(
mapStateToProps,
mapDispatchToProps
)(Component);
我认为您必须使用 @connect<StateProps, DispatchProps, OwnProps>
来装饰并返回一个接受 OwnProps
的类。
如果您查看 TS 中的 connect
s 实现
export declare function connect<TStateProps, TDispatchProps, TOwnProps>(...): ComponentDecorator<TStateProps & TDispatchProps, TOwnProps>
interface ComponentDecorator<TOriginalProps, TOwnProps>
(component: ComponentClass<TOriginalProps> | StatelessComponent<TOriginalProps>): ComponentClass<TOwnProps>;
connect<...>
返回一个ComponentDecorator
,它在传递组件时(在您的情况下,这是在将装饰器转出时透明地完成),而不管StateProps
和DispatchProps
返回一个期望@987654338 的组件@。
connect
(非泛型)返回InferableComponentDecorator
export interface InferableComponentDecorator
<P, TComponentConstruct extends (ComponentClass<P> | StatelessComponent<P>)>(component: TComponentConstruct): TComponentConstruct;
它尝试根据提供给组件的道具推断道具,在您的情况下,这是所有道具的组合(OwnProps
从上面变为ComponentProps
)。
【讨论】:
非常感谢!那成功了。它不适用于@connect
装饰器,我还必须删除namespace
,但是当我使用常规的export default connect(...)
时,就像在您的示例中一样,它可以工作。当我使用装饰器时,我得到一个编译器错误Type 'Component<IOwnProps, ComponentState>' is not assignable to type 'WbUserEditComponent'. Property 'componentDidMount' is missing in type 'Component<IOwnProps, ComponentState>
。我认为这是装饰器的打字问题。
抱歉,我无法使用装饰器 - 我过去曾尝试使用装饰器,但无法让它们正常工作,很好。不过,很高兴我至少可以解决您的问题。
如果你明确地指定了 mapStateToProps 和/或 mapDispatchToProps 中的参数类型,那么 TypeScript 应该能够推断出 connect
中的泛型类型参数。我认为每个参数的第一个参数可以是any
,第二个参数的类型是ComponentOwnProps
@mikebridge 是的,state
实际上是所有 reducer 的组合模式,但 any
也可以工作(尽管你失去了一些很好的智能感知)。第二个参数是ComponentOwnProps
,是的。
不知道为什么这个解决方案对我来说不成功——这是什么打字版本和 react-redux 版本?【参考方案2】:
当然,您可以手动设置类型。但是使用起来很舒服,实际上是从connect
获得的。它有助于避免烦人的重复。
示例 1:
type Props = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>
class MyComponent extends React.PureComponent<Props>
...
const mapStateToProps = (state: ReduxState) => (
me: state.me,
)
const mapDispatchToProps = (dispatch: ReduxDispatch) => (
doSomething: () => dispatch(Dispatcher.doSomething()),
)
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)
现在我们直接从 redux state 和 action/dispatch 函数中获取类型。
一段时间后,我们将此示例简化为:
示例 2:
//// other file
import InferableComponentEnhancerWithProps from 'react-redux'
type ExtractConnectType<T> = T extends InferableComponentEnhancerWithProps<infer K, any> ? K : T
//// <= Save it somewhere and import
type Props = ExtractConnectType<typeof connectStore>
class MyComponent extends React.PureComponent<Props>
...
const connectStore = connect(
(state: ReduxState) => (
me: state.me,
),
(dispatch) => (
doSomething: () => dispatch(Dispatcher.doSomething()),
)
)
export default connectStore(MyComponent)
【讨论】:
【参考方案3】:简单来说,
组件应该清楚 props
应该来自父级和 connect
(redux)。
现在,connect()
可以向组件发出redux state
(您的应用状态)或action
作为prop
,组件的其余props
应该来自父组件。
按照建议,最好将组件 props 分成 3 个部分(ComponentStateProps
,ComponentDispatchProps
&
ComponentOwnProps
),然后在 connect()
中使用它们。并且,将这 3 个道具连接起来形成ComponentProps
。
我认为下面的代码会更好地理解。
// Your redux State
type SampleAppState =
someState: any;
;
// State props received from redux
type ChildStateProps =
propFromState: any;
;
// dispatch action received from redux (connect)
type ChildDispatchProps =
actionAsProp: () => void;
;
// Props received from parent
type ChildOwnProps =
propFromParent: any;
;
// All Props
type ChildProps = ChildStateProps & ChildDispatchProps & ChildOwnProps;
const ChildComponent = (props: ChildProps) =>
return <>/*....*/</>;
;
let ConnectedChildComponent = connect<
ChildStateProps,
ChildDispatchProps,
ChildOwnProps,
SampleAppState
>(
(appState: SampleAppState, ownProps: ChildOwnProps) =>
// Shape that matches ChildStateProps
return
propFromState: appState.someState,
;
,
(dispatch, ownProps: ChildOwnProps) =>
return bindActionCreators(
// Shape that matches ChildDispatchProps
actionAsProp: () => ,
,
dispatch,
);
,
)(ChildComponent);
const ParentComponent = () =>
return (
<>
<ConnectedChildComponent propFromParent='Prop Value' />
</>
);
;
【讨论】:
以上是关于typescript + redux:在父组件中排除 redux props的主要内容,如果未能解决你的问题,请参考以下文章
使用 TypeScript 在 React 组件外部调度 Redux Thunk 操作
强烈键入 react-redux 与 typescript 连接
带有 Typescript 和 react-redux 的有状态组件:“typeof MyClass”类型的参数不可分配给 Component 类型的参数
Typescript 无状态 HOC react-redux 注入道具打字
在 .NET Core 2.0 reactredux 模板项目中使用 react redux 和 typescript 将组件与 OwnProps 连接起来