I am using redux and typescript for my current webapp.
What is the best practice to define the props of a component which receives redux-actions via @connect
, but also props from parent?
Example:
// 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>;
}
}
As I see it I got 2 solutions.
Just pass the actions & state down through my whole app. But that would mean that my whole app gets re-rendered even when just some small child component would have to change. Or is it the redux way to listen in my top level component on all store changes? Then I would have to write a lot of logic inside
shouldComponentUpdate
for props which are no flat objects.Set the param in
IProps
ofMyChildComponent
optional like this:
-
// mychild.tsx
export namespace MyChildComponent {
export interface IProps {
propertyFromParent: string;
propertyFromRedux?: typeof MyAction; // This is the problem
}
}
Is there another way? Both of the above ways seem too messy in my eyes.
You need to split up your props - you'll need a
DispatchProps
,StateProps
, and anOwnProps
type. You'll also have to use TypeScript's generics withconnect
DispatchProps
are your action creators.StateProps
are your state props (duh) - these come frommapStateToProps
- the return type of that function should match this type.OwnProps
are props which are accepted (and perhaps expected) by your component. Optional props should be marked as optional in the interface.The way I do it (without decorators, but i'm sure it applies here) is
I think you have to use
@connect<StateProps, DispatchProps, OwnProps>
which will decorate and return a class which acceptsOwnProps
.If you look at
connect
s implementation in TSconnect<...>
returns aComponentDecorator
, which, when passed the component (in your case this is done transparently when transpiling the decorator out), regardless ofStateProps
, andDispatchProps
returns a component which expectsOwnProps
.connect
(non-generic) returnsInferableComponentDecorator
which attempts to infer the props based on the props supplied to the component, which in your case is the combination of all props (
OwnProps
becomesComponentProps
from above).