I've got a plain react-redux-powered form. I wish for there to be a form.container.tsx and a form.component.tsx, where form.container.tsx holds all the connections to redux state minus the Field's. I'm trying to wrap my container in react-redux's connect and then wrapping reduxForm within it to look something like TypeScript, redux-form and connect:
(ideal) form.container.tsx:
interface DummyFormContainerProps {}
export const DummyFormContainer: React.SFC<DummyFormContainerProps> = props => {
const submitForm = (formValues: object) => {
alert(formValues);
};
return (
<DummyForm
onSubmit={submitForm}
/>
);
};
const mapStateToProps = (state: State) => ({});
const mapDispatchToProps = (dispatch: object) => {
return {};
};
const mergeProps = (stateProps: State, dispatchProps: object | null, ownProps: object | void) =>
Object.assign({}, stateProps, dispatchProps, ownProps);
const formConfiguration = {
form: 'dummy-form',
forceUnregisterOnUnmount: true
};
export default connect(mapStateToProps, mapDispatchToProps)(
reduxForm(formConfiguration)(DummyFormContainer)
);
The above does not work, but if I take out the reduxForm() part, I'm left with a working container with no reduxForm Integration:
(working without reduxForm) form.container.tsx:
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(
DummyFormContainer
);
And I've tried different variations with reduxForms and connect, all not currently working:
(with classes) form.container.tsx:
export class DummyFormContainer extends React.Component<DummyFormContainerProps, void> {
submitForm = (formValues: object) => {
alert(formValues);
}
render() {
return (
<DummyForm
onSubmit={this.submitForm}
/>
);
}
}
const mapStateToProps = (state: State) => ({});
const mapDispatchToProps = (dispatch: object) => {
return {};
};
const mergeProps = (stateProps: State, dispatchProps: object | null, ownProps: object | void) =>
Object.assign({}, stateProps, dispatchProps, ownProps);
const formConfiguration = {
form: 'business-registration',
};
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(
reduxForm(formConfiguration)(DummyFormContainer) // ERROR
);
error:
./src/modules/dummy-form/dummy-form.container.tsx
(100,32): error TS2345: Argument of type 'typeof DummyFormContainer' is not assignable to parameter of type 'ComponentType<InjectedFormProps<{}, {}>>'.
Type 'typeof DummyFormContainer' is not assignable to type 'StatelessComponent<InjectedFormProps<{}, {}>>'.
Type 'typeof DummyFormContainer' provides no match for the signature '(props: InjectedFormProps<{}, {}> & { children?: ReactNode; }, context?: any): ReactElement<any> | null'.
(with stateless functional components) form.container.tsx:
export const DummyFormContainer: React.SFC<DummyFormContainerProps> = props => {
const submitForm = (formValues: object) => {
alert(formValues);
};
return (
<DummyForm
onSubmit={submitForm}
/>
);
};
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(
reduxForm(formConfiguration)(DummyFormContainer) // ERROR
);
error:
./src/modules/dummy-form/dummy-form.container.tsx
(100,3): error TS2345: Argument of type 'DecoratedComponentClass<{}, Partial<ConfigProps<{}, {}>>>' is not assignable to parameter of type 'ComponentType<(State & null & void) | (State & null & object) | (State & object & void) | (State ...'.
Type 'DecoratedComponentClass<{}, Partial<ConfigProps<{}, {}>>>' is not assignable to type 'StatelessComponent<(State & null & void) | (State & null & object) | (State & object & void) | (S...'.
Type 'DecoratedComponentClass<{}, Partial<ConfigProps<{}, {}>>>' provides no match for the signature '(props: (State & null & void & { children?: ReactNode; }) | (State & null & object & { children?: ReactNode; }) | (State & object & void & { children?: ReactNode; }) | (State & object & { children?: ReactNode; }), context?: any): ReactElement<any> | null'.
The form.component.tsx looks like this:
import * as React from 'react';
import Input from '../../components/input';
interface DummyFormProps {
onSubmit: (formValues: object) => void
}
export const DummyForm: React.SFC<DummyFormProps> = () => {
return (
<div>
<h1>DummyForm (no state)</h1>
<form>
<Input inputType="primary" />
</form>
</div>
);
};
export default DummyForm;
And the < Input > component is a regular React component.
Does anyone know how to properly connect reduxForm and react-redux's connect()?
Here's a fully typed example that allows initializing a form using
initialValues
and passing additional props (asIOwnProps
):sampleForm.tsx:
sampleForm.ts:
The trick here is to specify proper return type for
mapStateToProps
, otherwise compiler will be complaining like other authors pointed out.Now this form can be mounted like this:
I had the same problem and found it was caused by "@types/react-redux", remove this types definition file and everything works the way you would expect it to without any other side effects/type errors caused by not having that type-def-file.
I also ran into this issue trying to initialise my form from redux state, as per the example in https://redux-form.com/7.0.4/examples/initializefromstate/
I ended up getting around it by connecting the component at a higher level, eg:
component.tsx:
container.tsx:
I found that I was able to dismiss the error by providing the connect statement with empty
TStateProps
andTDispatchProps
objects.The one downside to this is that it forces us to blindly supply connect props but I felt that this was a more elegant solution than writing an override @types declaration.
To address this shortcoming, I was able to validate the types by providing connect with the correct interfaces versus empty objects; however, this method can only be done temporarily to check the bindings as it doesn't resolve the
DecoratedComponentClass
error.What we ended up doing was to close our eyes and override the default types with a type declaration file:
redux-forms.d.ts: