How to export mapStateToProps and Redux Form? - reactjs

I'm using Redux Form (ver. 6) for a log in page. What I would like to do is when the user fills out the form and clicks submit, grab the text from my state so that I can eventually dispatch an action with that email and password. However, I'm having trouble with exporting this component while using both connect from react-redux and Redux Form.
Using react-redux, connect wants to be exported like so when mapping state to props:
export default connect(mapStateToProps)(LogInForm)
However, Redux Form wants it's export set up like this:
export default reduxForm({
form: 'LogInForm',
validate,
})(LogInForm);
Is there a way to combine these two? I'd tried something like:
const reduxFormConfig = reduxForm({
form: 'LogInForm',
validate,
});
export default connect(mapStateToProps)(ReduxFormConfig)(LogInForm)
but it did not work.
Or Perhaps that's a better approach to handling this? Here is the full code from within my component:
import React from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import InputField from '../InputField';
import { validateLogInSignUp as validate } from '../../utils/validateForms.js';
const LogInForm = (props) => {
const {
handleSubmit,
pristine,
submitting,
} = props;
return (
<div>
<form onSubmit={handleSubmit}>
<Field
name="email"
type="email"
component={InputField}
label="email"
/>
<Field
name="password"
type="password"
component={InputField}
label="password"
/>
<div>
<button type="submit" disabled={submitting}>Submit</button>
</div>
</form>
</div>
);
};
const mapStateToProps = state => {
return {
loginInput: state.form,
};
};
// export default connect(mapStateToProps)(LogInForm)
// export default reduxForm({
// form: 'LogInForm',
// validate,
// })(LogInForm);
Any and all help is much appreciated. Thanks!

There still a way to combine these two in reduxForm v6:
reduxForm({
form: 'LogInForm',
validate,
})(LogInForm);
export default connect(mapStateToProps)(LogInForm)
Here is how:
import React, { Component } from 'react';
import { connect } from 'react-redux
class LogInForm extends Component {
...
}
function mapStateToProps(state) {
return { ... }
}
function mapDispatchToProps(dispatch) {
return { ... }
}
// First decorate your component with reduxForm
LogInForm = reduxForm({
form: 'LogInForm',
validate,
})(LogInForm);
// Then connect the whole with the redux store
export default connect(mapStateToProps, maDispatchToProps)(LogInForm)

Using redux-form you shouldn't need to access the state directly in your LoginForm. Instead, you should access the values in your parent component when the form is submitted:
// LoginForm.js
const LogInForm = (props) => {
...
};
export default reduxForm({
form: 'LogInForm',
validate,
})(LogInForm);
// Parent.js
import LoginForm from './LoginForm';
const handleSubmit = (values) => {
alert(JSON.stringify(values)); // { email: 'foo#bar.com', password: '1forest1' }
};
const Parent = (props) => {
return (
<LoginForm onSubmit={ handleSubmit } />
);
};
See https://github.com/erikras/redux-form/blob/master/examples/simple/src/index.js#L42 for a more complete example of a simple form.

You can try this :
import React from 'react';
import { connect } from 'react-redux' ;
import { reduxForm } from 'redux-form';
class Example extends React.Component {
...
}
function mapStateToProps(state) {
return { ... }
}
function mapDispatchToProps(dispatch) {
return { ... }
}
export default connect(mapStateToProps, mapDispatchToProps)(reduxForm({
form: 'formname',
validate
})(Example));

There are multiple threads with same issue. I recently posted my take with my solution here but with a few variations.
I used a class based component than a function based. I have found guides to specifically implement that way and explaining that the connect method of react-redux make it a Higher Order Component and as such it is best used with a class instance.
As mentioned by #doncredas, reduxForm and react-redux's connect method should be implemented separately, however, my implementation is as follows:
function mapStateToProps(state) {
return { form: state.form };
}
Signin = connect(mapStateToProps, actions)(Signin);
Signin = reduxForm({
form: 'signin'
})(Signin);
export default Signin;
[Added later] Another variation can be:
const form = reduxForm({ form: 'signin' });
export default connect(mapStateToProps, actions)(form(Signin));

Related

Redux/React. TypeError: Cannot call a class as a function

So, I have a problem with the error like said in the title of question. I saw this answer Getting "Cannot call a class as a function" in my React Project , but I do not have any problem with Component extends, as you can see it implemented in the code normally.
So, the question is - what is the problem is? I already cannot figure out..
/* COMPONENT */
import React from 'react';
import AddTodo from '../../Actions/AddTodo'
import TodoFormAdd from '../../Containers/TodoFormAdd'
class TodoForm extends React.Component{
constructor(props) {
super(props);
}
handleSubmit = (e) => {
e.preventDefault();
let input = document.querySelector('input').value;
TodoFormAdd(this.props.store, input);
input = '';
}
render() {
return (
<form id="tp" onSubmit={this.handleSubmit}>
<input type="text" placeholder="Your text" />
<button type="submit">Add todos</button>
</form>
);
}
}
export default TodoForm;
/* CONTAINER FOR A COMPONENT ABOVE */
import { connect } from 'react-redux';
import AddTodo from '../Actions/AddTodo'
import TodoForm from '../Components/TodoForm/TodoForm'
let TodoFormAdd = (store, input) => {
store.dispatch(AddTodo(input));
}
export default connect()(TodoForm);
The problem is you are exporting a component from /Containers/TodoFormAdd in this line:
export default connect()(TodoForm)
It looks like you want to pass the dispatch to your component. You need to pass a mapDispatchToProps function to your connect function:
const mapPropsToDispatch = dispatch => {
return {
todoFormAdd: input => dispatch(addTodo(input))
}
}
connect(null, mapDispatchToProps)(TodoForm)
Then from your component, you can call the dispatch.
//TodoForm Component
this.props.todoFormAdd(input)

Redux-form ownProperties is undefined

I have a redux-form that I'm connecting to the state as follows:
export default connect(mapStateToProps)(reduxForm({
form: 'MyForm',
validate // validation function given to redux-form
})(MyForm));
I now would like to get a slug value which is a react-router param (after navigating to the page like this:
browserHistory.push(`/mypage/${slug}`) ). However ownProps is empty:
const mapStateToProps = (state, ownProps) => {
console.dir(ownProps); // ISSUE: this is empty
const someField = selector(state, 'someField');
...
};
I tried different ways of connected to redux-form, but haven't been successful. Would really appreciate any hints on how to solve this.
I think I have understood your problem here. You need to connect the redux form in order to get values using selector. Please see example code below. Hope it will help you.
import React from 'react'
import { Field, reduxForm, formValueSelector } from 'redux-form'
import validate from './validate'
import example from './components/example'
import { connect } from 'react-redux'
function myForm ({
someProp,
exampleClick
}) {
function handleSubmit (e) {
}
return <form onSubmit={handleSubmit}>
<Field
name='name'
component={example}
submitBtnClicked={exampleClick} />
</form>
}
let thisForm = reduxForm({
form: 'myForm',
validate
})(myForm)
const selector = formValueSelector('myForm')
thisForm = connect(
state => {
const someField = selector(state, 'someField')
return ({someField})
}
)(thisForm)
export default thisForm
I solved it by adding react-router's withRouter around my connected component:
export default withRouter(connect(mapStateToProps)(reduxForm({
form: 'MyForm',
validate // validation function given to redux-form
})(MyForm)));

Redux-Form Initial values

So I'm trying to load a Redux Form pre populated with values from my store. However I'm unable to get anything back other than null. Been at this for several hours now and been reading over several SO examples trying different things and think I'm just at a wall on this.
Following this Redux Form Initializing from State example
Redux: 3.6.0
React-Redux: 5.0.3
Redux-Form: 6.6.3
React: 15.4.2
There is a parent component that is rendering a child component which is the form. For sake of brevity going to put in the bare minimum of code and make names as generic as possible. Everything loads fine but I think the issue relies in not properly connecting to my store. Or rather I should just be loading the data on a componentWillMount?
Parent Component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchUser } from '../actions/usersActions';
import ChildForm from './ChildForm.jsx'
#connect((store) => {
return{
user: store.users.user
}
})
export default class Parent extends Component{
componentWillMount(){
this.props.dispatch(fetchUser(this.props.match.params.id))
}
submit = (values) => {//do some stuff}
render(){
return(
<ChildForm onSubmit={this.submit} />
);
}
}
ChildForm:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { user } from './reducers/usersReducer.js';
class ChildForm extends Component{
render(){
console.log('FORM STATE >>>>>>>>>>', this.state); //Returns NULL
const { handleSubmit } = this.props;
return(
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="firstName">First Name</label>
<Field name="first_name" component="input" type="text"/>
</div>
<button type="submit">Submit</button>
</form>
);
}
}
ChildForm = reduxForm({
form: 'childForm',
enableReinitialize : true // I found this in another SO Example
})(ChildForm);
ChildForm = connect(
state => ({
user: state.user,
initialValues: state.user
}),
{ fetchUser }
)(ChildForm)
export default ChildForm;
enableReinitialize SO
usersReducer.js
export default function reducer(state={
user: {},
}, action){
switch (action.type){
case "FETCH_USER_FULFILLED":{
return{
...state,
user: action.payload
}
}
}
return state;
}
So this is where I'm at currently. Can get the page, form, and submit all work. However I can't seem to figure out how to get my Store values out and into the form fields. Any help would be greatly appreciated.
Looks like everything is wired up correctly but I wasn't pulling in the correct object in the store.
ChildForm = connect(
state => ({
initialValues: state.users.user
}),
{ fetchUser }
)(ChildForm)
...Always something little

ReduxForm decorator, no-class-assign error after ejecting create-react-app

I recently ejected my create-react-app and I'm receiving an error with ReduxForm that's preventing webpack compilation. Here's the error: "error 'NewComplaintModal' is a class no-class-assign". It appears to be associated with the redux form decorator at the bottom, but I can't find any other implementations in the redux-form docs. Any idea how to fix this?
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import { Field, reduxForm } from 'redux-form';
import DatePicker from "react-bootstrap-date-picker";
class NewComplaintModal extends Component {
close() {
this.props.actions.hideModal();
}
handleFormSubmit(formProps) {
this.props.actions.submitComplaint(formProps);
}
render(){
const { handleSubmit } = this.props;
const show = this.props.modalType ? true : false;
const RenderDatePicker = ({input, meta: {touched, error} }) => (
<div>
<DatePicker showClearButton={false} {...input} />
{touched && error && <span>{error}</span>}
</div>
);
return(
<div>
...
</div>
)
}
}
NewComplaintModal = reduxForm({
form: 'newComplaintModal'
})(NewComplaintModal);
function mapStateToProps(state) {
return {
modal: state.modal
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(NewComplaintModal)
First, you're declaring a class called NewComplaintModal like this
class NewComplaintModal extends Component {
...
}
Then, you're treating your NewComplaintModal, which is a class declaration, like it's a variable, like this:
NewComplaintModal = reduxForm({
form: 'newComplaintModal'
})(NewComplaintModal);
This is why you're seeing the specific error, that is complaining about a class being reassigned. To fix this, I would recommend skipping the middle step of using the reduxForm decorator before connect and just chaining them using the decorator syntax:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import { Field, reduxForm } from 'redux-form';
import DatePicker from "react-bootstrap-date-picker";
export class NewComplaintModal extends Component {
close() {
this.props.actions.hideModal();
}
handleFormSubmit(formProps) {
this.props.actions.submitComplaint(formProps);
}
render(){
const { handleSubmit } = this.props;
const show = this.props.modalType ? true : false;
const RenderDatePicker = ({input, meta: {touched, error} }) => (
<div>
<DatePicker showClearButton={false} {...input} />
{touched && error && <span>{error}</span>}
</div>
);
return(
<div>
...
</div>
)
}
}
function mapStateToProps(state) {
return {
modal: state.modal
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch)
};
}
// use decorators around an extension of your component
// to plug into redux and redux-form
#connect(mapStateToProps, mapDispatchToProps)
#reduxForm({ form: 'newComplaintModal' })
export default class NewComplaintModalContainer extends NewComplaintModal {}
If you don't want to use the decorator syntax, you can do something like this:
export default connect(mapStateToProps, mapDispatchToProps)(
reduxForm({ form: 'newComplaintModal' })(
NewComplaintModal
)
)
This approach essentially insulates your component from the configuration-like elements, namely anything related to interacting with the application state.
Hope this helps!
#jakee pointed why you are getting the error.
Another way you can do is to use the compose method of redux.
import { compose } from 'redux';
...
export default compose(
connect(mapStateToProps, mapDispatchToProps),
reduxForm({...}),
)(NewComplaintModal);
I found a less-than elegant solution: Instead of an uninitialized NewComplaintModal, declare a new variable with a different name and pass that variable to the return value of the connect function.
For example:
const NewComplaintModalTemp = reduxForm({
form: 'newComplaintModal'
})(NewComplaintModal);
export default connect(mapStateToProps, mapDispatchToProps)(NewComplaintModalTemp)
If anyone has a better solution or wants to share any other relevant info to help explain why this is necessary that would be greatly appreciated.

redux-form Field cannot enter text into input field

I am able to render an input field using redux-form, but I can't seem to type any text inside the field (the component seems to re-render with each keystroke). Below is the code, I created a really simple one to try to pinpoint the problem:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createStore, combineReducers } from 'redux';
import { Field, reduxForm, reducer as formReducer } from 'redux-form';
const reducers = {
// ... your other reducers here ...
form: formReducer // <---- Mounted at 'form'
};
const reducer = combineReducers(reducers);
const store = createStore(reducer);
class TestForm extends Component {
formSubmit(props) {
console.log('form submitted');
}
render() {
const { handleSubmit } = this.props;
return (
<div>
This is a test form.
<div>
<form onSubmit={handleSubmit(this.formSubmit.bind(this))}>
<Field name="firstField" component="input" type="text" />
<button type="submit">Submit</button>
</form>
</div>
</div>
);
}
}
TestForm = reduxForm({
form: 'testingForm'
})(TestForm);
export default TestForm;
I also literally copied and pasted the example from the official redux-form docs: http://redux-form.com/6.0.2/docs/MigrationGuide.md/ and I still encounter the same problem. Been trying to figure this out for some hours now. Could it be the version of redux or redux-form?
I'm using redux v3.5.2 and redux-form v6.2.1.
Would appreciate any help!
I had the same issue. The cause was that I failed to add the formReducer to the list of reducers I was using. To fix I added:
import { reducer as formReducer } from 'redux-form'
const RootReducer = combineReducers({
...
form: formReducer // <---- Mounted at 'form'
})
I think you need to look into the react-redux docs. This component needs to be placed inside a <Provider store={store}> component. Perhaps you are doing that outside this file and have just included the store initialization to be informative?

Resources