Very much looking for some help on this
Libs used: recompose, react-redux
The issue:
When composing redux's connect
with other HOC's with compose
I'm losing types within compose and trying to access from a text context ConnectedComponent.WrappedComponent
throws a type error: 'Property 'WrappedComponent' does not exist on type 'ComponentClass<{}, any>'
.
The Code
import React, { Fragment, SFC } from 'react';
import { connect } from 'react-redux';
import { branch, renderComponent, compose } from 'recompose';
import { RootState } from '../../types/state';
interface StateProps {
foo?: boolean;
}
const Component: SFC<StateProps> = ({ children }) => <Fragment>{children}</Fragment>;
const mapStateToProps = (state: RootState): StateProps => ({
foo: state.foo
});
export default compose<StateProps, {}>(
connect(mapStateToProps),
branch<StateProps>(
({ foo }) => !initialised,
renderComponent(() => <div className="branched" />)
)
)(Component);
Then Accessing the wrapped component errors as the types are not carried through:
const Gateway = ConnectedGateway.WrappedComponent;
^^^^^^^^^^^^^^^^^ ->'Property 'WrappedComponent' does not exist on type 'ComponentClass<{}, any>'
There are a few solutions I've found that seem to work, changing the composition for example
connect(mapStateToProps)(compose(
// Stuff Here
)(Component))
Keeps the types, but shouldn't be necessary as from a FP standpoint it's exactly the same as compose(connect, branch)(Component)
The other is extending ComponentClass
and exporting as unknown as IComponent
which feels like a lot of hoops to jump through:
import React, { Fragment, SFC, ComponentClass } from 'react';
import { connect } from 'react-redux';
import { branch, renderComponent, compose } from 'recompose';
import { RootState } from '../../types/state';
interface StateProps {
foo?: boolean;
}
interface IComponent extends ComponentClass<StateProps, {}> {
WrappedComponent: SFC<StateProps>;
}
const Component: SFC<StateProps> = ({ children }) => <Fragment>{children}</Fragment>;
const mapStateToProps = (state: RootState): StateProps => ({
foo: state.foo
});
export default (compose<StateProps, {}>(
connect(mapStateToProps),
branch<StateProps>(({ foo }) => !foo, renderComponent(() => <div className="branched" />))
)(Component) as unknown) as IComponent;
Any thoughts would be appreciated.
EDIT: BETTER SOLUTION
You can cast it to the correct type using ConnectedComponentClass
which simplifies the typing a lot.
import React, { Fragment, SFC, ComponentClass } from 'react';
import { connect, ConnectedComponentClass } from 'react-redux';
import { branch, renderComponent, compose } from 'recompose';
import { RootState } from '../../types/state';
interface StateProps {
foo?: boolean;
}
const Component: SFC<StateProps> = ({ children }) => <Fragment>{children}</Fragment>;
const mapStateToProps = (state: RootState): StateProps => ({
foo: state.foo
});
export default compose<StateProps, {}>(
connect(mapStateToProps),
branch<StateProps>(({ foo }) => !foo, renderComponent(() => <div className="branched" />))
)(Component) as ConnectedComponentClass<SFC<StateProps>, {}>;