Below is my RequestAnInvite redux-form. The problem is when I submit the form, submitting is never changed to true. You can see I have a log below, which is always outputting false.
What am I doing wrong with redux-form to cause submitting to never set to true when I submit the form?
class RequestAnInvite extends React.Component {
componentDidMount() {
this.props.dispatch(loadTitles());
}
handleSubmit(data) {
console.log(data);this.props.dispatch(requestInvitationsActions.createInvitationRequest(data));
}
render() {
const { handleSubmit, submitting } = this.props;
console.log('submitting: ' + submitting);
return (
<div className="container-fluid h-100">
<form onSubmit={handleSubmit(this.handleSubmit.bind(this))}>
<Field
name="email"
type="text"
component={renderField}
label="Email"
placeholder="xxx#acme.com"
/>
<p>submitting: {submitting}</p>
<div className="form-group form-group-actions">
<button type="submit" className="btn btn-primary" disabled={submitting}>
{submitting ? 'Requesting...' : 'Request an Invite'}
</button>
</div>
</form>
</div>
);
}
}
RequestAnInvite = reduxForm({
form: 'RequestAnInvite',
validate,
})(RequestAnInvite);
const mapStateToProps = state => {
return {
titles: state.titles
};
};
const mapDispatchToProps = (dispatch) => bindActionCreators({
...requestInvitationsActions,
}, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(RequestAnInvite);
Update 1
handleSubmit(data) {
this.props.createInvitationRequest(data)
.then((response) => {
console.log(response)
}, (error) => {
});
}
From redux-form docs:
Whether or not your form is currently submitting. This prop will only work if you have passed an onSubmit function that returns a promise. It will be true until the promise is resolved or rejected.
Your handleSubmit is just dispatching an action so it has no way of knowing when it is submitting
Related
I'm trying to make a SubmissionError show up in my redux-form form (if backend server response isn't status code 200), but I'm not sure how to probably due to my beginner level experience in React, Redux, React Redux, Redux Form, and Axios. I've already tried a lot of things (and currently reverted back from them) by looking through various SO posts and Github issues. If someone/people can help me out, that'd be greatly appreciated!
The current error I'm getting is this promise uncaught message:
error screenshot
Although there are more files in my project, here are the relevant ones here (minus the import statements):
ssoLoginPage.js
const renderField = (field) => {
const { input, label, name, type, meta: { touched, error } } = field;
const placeholder = `Enter ${label} here`;
return(
<div className="slds-form-element slds-m-bottom--large">
<label className="slds-form-element__label" htmlFor={label}>
{label}
</label>
<div className="slds-form-element__control">
<input
key={name}
id={name}
className="-input"
type={type}
placeholder={placeholder}
{...field.input}
/>
</div>
{touched && error && <span>{error}</span>}
</div>
);
};
class SsoLoginPage extends React.Component {
constructor(props) {
super(props);
}
onSubmit(values) {
this.props.ssoLogin(values, () => {
throw new SubmissionError({_error: "One or more credentials are incorrect"});
});
}
render() {
console.log("ssoLoginStatus:",this.props.ssoLoginStatus);
const { error, handleSubmit} = this.props;
return (
<IconSettings iconPath="/slds-static-2.6.1/assets/icons">
<div>
<Modal
title="SSO Login"
isOpen={!this.props.ssoLoginStatus}
onRequestClose={this.toggleOpen}
dismissOnClickOutside={false}
>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Field
name="username"
type="text"
component={renderField}
label="Username"
/>
<Field
name="password"
type="password"
component={renderField}
label="Password"
/>
<Field
name="verificationCode"
type="password"
component={renderField}
label="Verification Code"
/>
{error && <strong>{error}</strong>}
<Button type="submit" label="Login" variant="brand" />
</form>
</Modal>
</div>
</IconSettings>
);
}
}
function mapStateToProps({ssoLoginStatus}) {
return ssoLoginStatus;
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ssoLogin},dispatch);
}
export default reduxForm({
form: "ssoForm" // a unique identifier for this form
})(
connect(mapStateToProps,mapDispatchToProps)(SsoLoginPage)
);
settings.setAppElement("#root");
reducersIndex.js
const rootReducer = combineReducers({
ssoLoginStatus: ssoReducer,
form: formReducer
});
export default rootReducer;
ssoReducer.js
const ssoProcess = (state, action) => {
console.log("ssoLogin action:",action);
switch(action.payload.status) {
case 200:
//everything's okay
console.log("reaching 200 case?");
return {...state, "ssoLoginStatus": true};
default:
//not sure what to do here currently
return state;
}
};
export default function(
state={"ssoLoginStatus": false}, action) {
console.log("action type:",action.type);
switch (action.type) {
case SSO_LOGIN:
console.log("return something here hopefully");
return ssoProcess(state, action);
default:
return state;
}
}
actionsIndex.js
export const SSO_LOGIN = "SSO_LOGIN";
export const BASE_URL = "http://localhost:8080";
export function ssoLogin (values, callback) {
console.log("went inside ssologin action creator!",
values.username,
values.password,
values.verificationCode);
const url = `${BASE_URL}/ssologin?username=${values.username}&password=${values.password}&verificationCode=${values.verificationCode}`;
console.log("url w/ params:",url);
const request = axios
.post(url,{responseType: "json"})
.then(response => {
console.log("axios response: ",response);
return response;
})
.catch(error => {
console.log("sso error: ",error);
return callback();
});
return {
type: SSO_LOGIN,
payload: request
};
}
I am trying to test a Redux action and need assistance with testing an action with side-effects.
Here is my action :
export function login(email, password) {
return dispatch => {
dispatch(setLoginSuccess(false));
loginApi(email, password, error => {
dispatch(setLoginPending(false));
if (!error) {
dispatch(setLoginSuccess(true));
} else {
dispatch(setLoginError(error));
}
});
}
}
Below is the loginApi function to authenticate user :
export function loginApi(email, password, callback) {
if (email === 'test#test.com' && password == '123') {
return callback(null);
} else {
return callback(new Error('Please provide valid email and password'));
}
};
Additionally, I am facing an issue while simulating a form submit in my component with Enzyme and Jest.
Here is the code for the same :
render() {
let {email, password, emailValid} = this.state;
let {isLoginPending, isLoginSuccess, loginError} = this.props;
return (
<div className="col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2 col-xs-10 col-xs-offset-1">
<h3 className="text-center">Login</h3>
<form className="login-form" onSubmit={this.handleSubmit.bind(this)}>
<div className={emailValid? "form-group has-success" : (emailValid == undefined)? "form-group": "form-group has-error"}>
<label>Email address</label>
<input type="email" name="email" className="form-control" ref="userEmail"
placeholder="Enter your email" onChange={this.handleChange.bind(this)}/>
</div>
{/* Checking if email valid or not */}
{this.props.emailValid? "" : (this.props.emailValid == undefined)? "" : <p className="text-danger">Please provide a valid email!</p>}
<div className="form-group">
<label>Password</label>
<input type="password" name="password" className="form-control" ref="userPassword"
placeholder="Enter your password" onChange={this.handleChange.bind(this)}/>
</div>
<button type ="submit" className="btn btn-primary btn-block" disabled={!this.props.emailValid}>Get Started</button>
{/* Displaying error messages */}
{ loginError && <div className="auth-error-msg"><p className="text-danger">{loginError.message}</p></div> }
</form>
</div>
);
};
Here is the code for the handleSubmit event:
handleSubmit(e){
e.preventDefault();
this.props.login(this.refs.userEmail.value, this.refs.userPassword.value);
this.setState({
email: '',
password: ''
});
}
I am trying to simulate the Submit event in this way :
it('should render 1 error block on submitting invalid form', () => {
// Render a checkbox with label in the document
const spy = jest.fn();
const component = shallow(<Login login={spy}/>);
const form = component.find('form').simulate('submit');
});
But it currently throws an error as it cannot find preventDefault. How do I test this event?
I would recommend you to split the testing. Submitting the form and testing the actions are two separate things. For testing the action with jest, you need to dispatch the action to a mock store, and see which is the final state of the store. Something like this:
describe('actions', () => {
let store
beforeEach(() => {
store = mockStore({})
})
it('should dispatch the correct actions', () => {
const expectedActions = [
{ type: 'action1', ...arguments },
{ type: 'action2', ...arguments }
]
store.dispatch(login('user', 'password'))
expect(store.getActions()).toEqual(expectedActions)
})
})
you can do multiple test cases, adapting the expected actions to what you passed as parameters.
For creating a mock store, there are multiple packages that can do the job. Here is one example with support for thunks:
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
const middlewares = [ thunk ]
const mockStore = configureMockStore(middlewares)
export default mockStore
I personally would not spend too much effort testing that the form submits. At the end, that is a standard html thing, so I would instead focus on the components that you have built yourself.
And another tip: If you have gone through all the trouble of using redux, don't fall back to normal state. That setState that you have would be much more easily implemented, and tested, by using a normal reducer and getting that into your state as well.
I'm using redux-form in my React / Redux application, and I'm trying to figure out how to dispatch an action on submit.
I have been able to trigger the handleSubmit function, and the submit function that I pass to it is executed, but the 'submitFormValues' action is not called.
I've tried using mapDispatchToProps and connect(), but that doesn't work either.
The Redux-Form actions execute (START_SUBMIT, STOP_SUBMIT, SET_SUBMIT_SUCCEEDED), but my own action creator is never executed.
Here's the form:
import React from "react";
import { Field, reduxForm } from "redux-form";
import {submitFormValues} from "../../../actions/BasicFormActions";
function submit(values) {
//Can log the values to the console, but submitFormValues actionCreator does not appear to be dispatched.
return new Promise(function(resolve) { resolve(submitFormValues(values))} )
}
const renderField = ({ input, label, type, meta: {touched, error} }) => (
<div>
<label>{label}</label>
<div>
<input {...input} placeholder={label} type={type}/>
{touched && error && <span>{error}</span>}
</div>
</div>
)
const BasicForm = (props) => {
const { error, handleSubmit, pristine, reset, submitting } = props;
return (
<form onSubmit={handleSubmit(submit)}>
<Field name="firstName" type="text" component={renderField} label="First Name"/>
<Field name="lastName" type="text" component={renderField} label="Last Name"/>
{error && <strong>{error}</strong>}
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear</button>
</div>
</form>
)
}
export default reduxForm({
form: "basicForm",
submit
})(BasicForm)
Here's the action Creator (uses Thunk). I'm able to dispatch these actions successfully, just not from the form.
export const submitFormValues = (values) => (dispatch) =>
getData("submitApproveForm", values).then(response => {
dispatch(formSubmissionError(response))
}).catch(error => {
throw (error);
});
const formSubmissionError = (response) =>
({
type: types.FORM_SUBMISSION_ERROR,
basicFormResponse: { ...response}
});
export const getData = (apiName, args) =>
fetch(settings.basePath + getUrl(apiName, args))
.then(response =>
response.json()
).catch(error => {
return error;
});
Finally my reducer:
import * as types from "../actions/ActionsTypes";
import initialState from "./initialState";
const basicFormReducer = (state = initialState.basicFormResponse, action) => {
switch (action.type) {
case types.FORM_SUBMISSION_ERROR:
return {...state, "errors": action.basicFormResponse}; // returns a new state
case types.FORM_SUBMISSION_SUCCESS:
return {...state, "successes": action.basicFormResponse}; // returns a new state
default:
return state;
}
};
export default basicFormReducer;
Thank you in advance for your help.
Redux Form does not dispatch anything in the onSubmit callback. This is because we make no assumptions regarding how you would like to deal with your form submissions.
You can, however, use the dispatch argument and... dispatch your action!
function submit(values, dispatch) {
return dispatch(submitFormValues(values));
}
When I am submiting the form submitSucceeded props goes true, pristine is also working fine but submitting props not change on submit form. I have attached related code. Please suggest me how I can fix this issue.
import React from 'react'
import { Field, reduxForm } from 'redux-form'
import FileInput from '../FileInput'
import 'react-widgets/dist/css/react-widgets.css';
import './reactForm.css';
const EditForm = (props) => {
const { handleSubmit, submitSucceeded, pristine, submitting, owners, cities, compound, avatarUrl, changeAvatar } = props;
return (
<form onSubmit={handleSubmit}>
<div className="row padding-20-0">
<div className="col-md-4">
<div className="box-upfile cursor" style={{backgroundImage: `url(${avatarUrl})`}} >
<div className="editImgComp" >
<i className="sprite-icon icon-030" onClick={()=>{changeAvatar(null); props.change('avatar', null)}}/>
<label html="imageBrowse">
<FileInput
onDone={(file)=> {changeAvatar(file.file); props.change("avatar", file.file)}}
type="file" className="hidden" id="imageBrowse"/>
<i className="sprite-icon icon-031"/>
</label>
</div>
</div>
</div>
</div>
<div className="row">
<div className="text-right col-xs-6">
{
submitSucceeded ?
<button type="button" className="btn ls-btn-red cursor" disabled={pristine || submitting || submitSucceeded}>
<i className='fa fa-circle-o-notch fa-spin'></i> Saving
</button>
:
<button type="submit" className="btn ls-btn-red cursor" disabled={pristine || submitting} onClick={handleSubmit} >Save</button>
}
</div>
</div>
</form>
)
}
export default reduxForm({
form: 'compoundForm' // a unique identifier for this form
})(EditForm)
Container:-
handleSubmit(data) {
this.props.dispatch(compoundSave(data));
}
Action:-
export function compoundSave(data) {
const id = data.id;
const config = {
method: 'put',
body: JSON.stringify({compound: data}),
};
return callApi('/v1/compounds/'+id, {}, config, compoundSaveRequest, compoundSaveSuccess, compoundSaveFailure);
}
Call Api method:-
`export function callApi(path, params, config, request, onRequestSuccess, onRequestFailure) {
const API_ROOT = 'http://api.dev.leasing.clicksandbox.com:8080';
const idToken = localStorage.getItem('id_token');
let url = API_ROOT+path;
url = buildUrlWithQueryString(url, params);
return dispatch => {
dispatch(request);
return fetch(url, config)
.then(checkStatus)
.then(parseJSON)
.then((json) => {
if (!json.success) { // (response.status < 200 || response.status > 300)
json.error &&
Toastr.error(json.error);
dispatch(onRequestFailure(json));
} else {
json.message &&
Toastr.success(json.message);
dispatch(onRequestSuccess(json));
}
}).catch((error) => {
const exceptionMessage = {
success: false,
error: "Something went wrong!"
}
dispatch(onRequestFailure(exceptionMessage));
});
};
}`
Please let me know if I need to explain more.
For any one else arriving here a year later like me looking for answers, and without seeing the Container code... i can infer that the problem is that you defined the method handleSubmit in the Container and send it as a prop to EditForm. The problem with that is that a Component that has the reduxForm() HOC applied, in this case EditForm, will generate its own handleSubmit prop, and therefore a conflict arises, leading to wrong behaviour of the form when submitting.
In order to fix the issue, you should have send the prop to EditForm with a different name say
<EditForm onSubmit={this.onSubmit} />
And then inside the EditForm component use it like:
...
<form onSubmit={handleSubmit(onSubmit)}>
That way the prop submitting of the reduxForm component will work if the submit handler returns a promise.
you should call handleSubmit() method with handler passed to component :
<form onSubmit={handleSubmit(this.props.onSubmit)}>
I'm struggling to understand where is the best practice to handle success submit and navigate to the next page.
I'm working on a login form:
class LogInComponent extends Component {
render() {
const {dispatch} = this.props;
const {loginError, handleSubmit, pristine, reset, submitting} = this.props;
return (
<form onSubmit={handleSubmit((values) => dispatch(login(values))) }>
<Field name="username" type="text" component={renderField} label="Username" />
<Field name="password" type="password" component={renderField} label="Password" />
{loginError && <strong>{loginError}</strong>}
<div>
<button type="submit" disabled={submitting}>Log In</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}
And the action:
export function login(values) {
const email = values.username;
const password = values.password;
return dispatch => {
dispatch(loginSubmit());
firebaseAuth.signInWithEmailAndPassword(email, password).then(function(user) {
dispatch(signInSuccess(user));
}).catch(function(error) {
dispatch(signInError(error));
});
};
}
export function loginSubmit() {
return {
type: SIGN_IN_SUBMIT
};
}
export function signInError(error) {
return {
type: SIGN_IN_ERROR,
payload: error.message
};
}
export function signInSuccess(user) {
return {
type: SIGN_IN_SUCCESS,
payload: user
};
}
If the response was successful, I would like to navigate to the next page. But where should the navigation be? not from the reducer or action, so only from component, but the action does not return response by design..
Am I missing something?
Create a composing function to couple your login code and navigation logic, and dispatch that function on form submit.
Modify the actions file as below:
import { browserHistory } from './react-router';
// no export needed, this is a #private function
function login(values) {
const email = values.username;
const password = values.password;
return dispatch => {
dispatch(loginSubmit());
return firebaseAuth.signInWithEmailAndPassword(email, password)
.then((user) => dispatch(signInSuccess(user)))
.catch((error) => dispatch(signInError(error)));
};
}
export function loginAndRedirect(loginParams) {
return dispatch => dispatch(login(loginParams))
.then(() => browserHistory.push('/success/path'))
.catch(() => browserHistory.push('/failure/path'));
}
// ... other actions
Now in our component we will do this:
<form onSubmit={handleSubmit((values) => dispatch(loginAndRedirect(values))) }>