redux-form Field cannot enter text into input field - reactjs

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?

Related

Form input not changing when using react-testing-library on redux-form

I am trying to test that when a user inputs text into an input that a button changes from disabled to enabled. It clearly works in the browser, but I can not get the test to pass. I am using redux-form and react-testing-library. However, if I use a native input rather than redux-form's Field, the test passes.
I created a repo with minimal code to duplicate the problem.
Since you are not using the reducer from redux-form in your tests, its props will not be passed down to your component correctly. It's best if you provide a reducer which resembles what your app is actually using. The following code works.
import React from "react"
import { Provider } from "react-redux"
import { combineReducers, createStore } from "redux"
import { reducer as reduxFormReducer } from "redux-form"
import { fireEvent, render } from "#testing-library/react"
import SimpleForm from "./SimpleForm"
// Create a reducer that includes the form reducer from redux-form
const reducer = combineReducers({
form: reduxFormReducer,
})
const store = createStore(reducer)
it("test that button is not disabled when input is entered", () => {
const { getByLabelText, getByText } = render(
<Provider store={store}>
<SimpleForm />
</Provider>,
)
const input = getByLabelText("Name")
const submit = getByText("Submit")
fireEvent.change(input, { target: { value: "test input" } })
expect(submit).not.toBeDisabled()
})

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

Redux - reducer not getting called

I'm trying to learn Redux. I followed an online tutorial and used their example and it worked fine. Then I made another small test app and it worked fine. Now I'm trying another small test app and I'm stuck on this error and have no idea why.
The app is simply a input box and a button, and when you click the button it adds whatever's in the box to a list that is displayed below.
However, the reducer isn't updating the state. I've looked at the common problems that cause this and can't seem to find those errors in mine, my reducer doesn't change the state and I've bound the dispatch and so on. Maybe I've just mispelled something somewhere (or something small and stupid like that) but I just can't get it working.
So I tried to change it so that it just displays whatever you type in the box below and it STILL doesn't work. Anyone know why the reducer isn't activating?
index.js (main)
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import promise from 'redux-promise';
import createLogger from 'redux-logger';
import allReducers from './reducers';
import App from './components/App';
const logger = createLogger();
const store = createStore(
allReducers,
applyMiddleware(thunk, promise, logger)
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
App.js
import React from 'react';
import NameList from '../containers/name-list.js'
import Input from '../containers/input.js'
const App = () => (
<div>
<Input />
<hr />
<NameList />
</div>
);
export default App;
index.js (action)
export const addName = (name) => {
return {
type: 'NAME_ADDED',
payload: name
}
};
reducer-add-name.js
export default function (state = null, action) {
switch (action.type) {
case 'NAME_ADDED':
return action.payload;
break;
}
return state;
}
index.js (reducer combiner)
import {combineReducers} from 'redux';
import AddNameReducer from './reducer-add-name';
const allReducers = combineReducers({
nameList: AddNameReducer
});
export default allReducers
input.js
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {addName} from '../actions/index'
class Input extends Component {
render() {
return(
<div>
<input id='name-input' />
<button id='add-name-button' onClick={() => addName(document.getElementById('name-input').value)}>Add name</button>
</div>
);
}
}
function mapStateToProps(state) {
return {
nameList: state.nameList
};
}
function matchDispatchToProps(dispatch) {
return bindActionCreators({addName: addName}, dispatch);
}
export default connect(mapStateToProps, matchDispatchToProps)(Input);
name-list.js
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
class NameList extends Component {
getNameList() {
//This is where I'll loop through this.props.nameList and build the list of elements
return this.props.nameList;
}
render() {
return(
<div>Current list : {this.getNameList()}</div>
);
}
}
function mapStateToProps(state) {
return {
nameList: state.nameList
};
}
export default connect(mapStateToProps)(NameList);
Thanks for any help!
I think your action hasn't been dispatched.
In your input.js
On button tag, change
onClick={() => addName(document.getElementById('name-input').value)}
to
onClick={() => this.props.addName(document.getElementById('name-input').value)}
Thing is action should be passed through mapDispatchToProps and 'bindActionCreators(actions, dispatch)' will wrap your action with 'dispatch', and pass through your component via props 'addName'. (as defined in your mapDispatchToProps)
An action alone is like a bullet (just return object) you will need something to fire it (dispatch)
In your case
import {addName} from '../actions/index' <--- a bullet
has been declared and your onClick, without dispatch, it would only return a mere object, not dispatching it to reducer.
addName() // <--- Just a bullet
But
this.props.addName() // <--- bullet with a gun
is from mapDispatchToProps / bindActionCreators... it has dispatch() wrapped around, that is the one we would use.
You should combine existing state with the new action payload in reducer-add-name.js instead of just returning the payload i.e.
return Object.assign({}, state, {
todos: [
...state,
{
action.payload
}
]
})
Not the OP's question but for people who just searched Google for "reducer not getting called", make sure you call your action creator:
dispatch(action())
not
dispatch(action)
You have to pass in the action, not the action creator. With useSelect in Redux Toolkit I usually don't have parens in the arg so I forgot to put the parents in the call to dispatch.
This happened to me today.
I forgot to add my new action to my destructured props in React while using the class method
const { newAction } = this.props

How to export mapStateToProps and Redux Form?

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));

Resources