Dealing with forms in React can be troublesome sometimes. You need to take care of proper state management, smooth user experience, simple form declarations, reusable components, etc. But the reason I moved to Redux Form was validation. I wanted to make the implementation of forms easier and quicker without adding a lot of code to my page's components. But I hit the wall when I tried to connect Redux Form with React Redux.
It wasn't the first time I was doing this so I did not expect any difficulties. I was wrong. With up to date dependencies I tried many different approaches while connecting reduxForm and connect() function. From putting one inside the other to connecting exported component with each function separately. Nothing was working properly. Regardless of what I tried, I ended up with this error message:
Uncaught Invariant Violation:
You must pass a component to the function returned by connect.
Instead received {"displayName":"ReduxForm",
"defaultProps": {destroyOnUnmount":true, "form":"loginForm", etc.}}
I decided to look for help on the Internet. There was no straight answer helping me with what am I missing or how should I do it properly. And apparently, people still struggle with this problem. Checking StackOverflow and other forums resulted in finding only outdated working versions. Even official example from redux-form documentation wasn't working! I've decided to try baby steps method by connecting each part separately in different files. I ended up with an additional file container.tsx where I connected the component at a higher level. Let's see what happened.
First, let's connect a form component I created earlier with Redux Form:
Next, letβs see how we can implement a container for that component:
import * as React from 'react';
import { connect } from 'react-redux';
import { LoginForm, IProps } from './LoginForm';
import actions from './actions';
import selector from './selector';
const LoginContainer = (props:IProps) =>
<LoginForm {...props} />;
export default connect(selector, actions)(LoginContainer);
What we are doing is we take the created LoginComponent function and wrap it with reduxForm. In container.tsx file LoginContainer function will pass our props to the imported component. In the end, we use connect() to create communication between our container and store props. The most tricky part starts if we want to add TypeScript to our form and do not use any at all! Leaving basic component's props will throw the error saying our props do not exist in this component. But we just passed it, right?
TS2559: Type '{ loginAction: () => any; }' has no properties in common with type
'IntrinsicAttributes & IntrinsicClassAttributes<FormInstance<{},
Partial<ConfigProps<{}, {}, string>>, string>> & Readonly
<Partial<ConfigProps<{}, {}, string>>> & Readonly<...>'.
It took me at least an hour to figure it out and answer "why?". I decided to reach for help to the Internet again but the only answers I found weren't clear what was happening and where I should put my props. After a couple of tries, I finally found a solution which was sensible and I was happy with. First of all, in LoginForm.tsx file we need to create an interface IFormProps with the names of the fields that are in our form. Next we need to import type definition of InjectedFormProps from redux-form library and combine it with our props interface. InjectedFormProps accepts two parameters: FormProps = {} and P={}. The first one represents form data which will be put in the form and the second one is our component's props. Theoretically, you can leave both objects empty. But that's not why we are using TypeScript, right?
To sum up, if you want to connect Redux Form with React Redux do yourself a favour and create another file which will be a bridge between those two libraries. And don't forget about InjectedFormProps to combine it with TypeScript. And if you want to see an example check out my working implementation: Redux Form with TypeScript. Good luck and see you next time! ππ»
This project is an example of working implementation of Redux Forms and TypeScript. Whole process is described in my article Connecting Redux Form and Connect.