Why won't Axios give me a response with Redux actions? - reactjs

I am trying to make a simple signup form. I have a redux form that I'm trying to send some user data to my express backend with. I am trying to do this through redux actions via this redux action:
Ultimately, I'd like to receive a response, and redirect or give errors if necessary. The backend seems to receive the data and can validate, but axios receiving the response to let redux know to update the state. Any ideas?
Edit: I also tried putting the axios request inside of the signupForm itself, and still had issues(wasn't able to get a response).
Edit: Here is the repo if you'd like to see all the files: https://github.com/capozzic1/react-blog
redux signup action:
import axios from 'axios';
import { SubmissionError } from 'redux-form';
/* eslint-disable */
export const signUp = userData => dispatch => axios.post('/api/users', userData)
.then((response) => {
dispatch({ type: 'SIGNUP_REDIRECT_YES ', payload: true})
// this.props.history.go('/');
console.log(response);
})
.catch((error) => {
console.log(error.response);
dispatch({ type: 'SIGNUP_REDIRECT_NO ', payload: false})
throw new SubmissionError({ _error: 'Login failed!' });
});
Also with this signup form component (redux-form):
class SignUpForm extends React.Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(userData) {
this.props.signup(userData);
}
render() {
const { error, handleSubmit, pristine, reset, submitting } = this.props;
return (
<form >
{error && (<strong>{error}</strong>)}
<Field name="username" type="text" component={renderField} label="Username" />
<Field name="email" type="email" component={renderField} label="Email" />
<Field name="password" type="text" component={renderField} label="Password" />
<Field name="passwordConfirm" type="text" component={renderField} label="Enter password to confirm" />
<div>
<button type="button" disabled={submitting} onClick={handleSubmit(this.onSubmit)}>Sign Up</button>
<button type="button" onClick={reset}>Clear Values</button>
</div>
</form>
);
}
}
export default reduxForm({
form: 'signUpForm',
})(SignUpForm);
This form is being fed by a container component(thought this was a standard pattern? Let me know if it's not).
Sign up page container:
const mapDispatchToProps = dispatch => ({
signup: (user) => {
dispatch(signUp(user));
},
});
const SignUp = props => (
<Layout>
<SignUpForm signup={props.signup} />;
</Layout>
);
export default connect(null, mapDispatchToProps)(SignUp);
Here is the sign up reducer:
export default function reducer(state = {
signupRedirect: false,
}, action) {
switch (action.type) {
case 'SIGNUP_REDIRECT_YES': {
return {
...state, signupRedirect: action.payload,
};
}
case 'SIGNUP_REDIRECT_NO' : {
return {
...state, signupRedirect: action.payload,
};
}
}
return state;
}
Here is my store:
import { applyMiddleware, createStore, compose } from 'redux';
import { createLogger } from 'redux-logger';
import thunk from 'redux-thunk';
import promise from 'redux-promise-middleware';
import reducer from './reducers';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(
applyMiddleware(promise(), thunk, createLogger()),
));
export default store;

It doesn't work because your anonymous function returned by signUp function returns a promise. Use brackets to avoid default behaviour.
export const signUp = userData => dispatch => {
axios.post('/api/users', userData)
.then((response) => {
dispatch({ type: 'SIGNUP_REDIRECT_YES ', payload: true})
// this.props.history.go('/');
console.log(response);
})
.catch((error) => {
console.log(error.response);
dispatch({ type: 'SIGNUP_REDIRECT_NO ', payload: false})
throw new SubmissionError({ _error: 'Login failed!' });
});
}

Related

Error in returned value of mapDispatchToProps

I'm getting the following error for my code:
Uncaught (in promise) TypeError: _this2.props.login is not a function
at Login.js:37 (for a valid user I get the message "Authorized" without any problem).
When I console log the props what I get is this**{match: {…}, location: {…}, history: {…}, staticContext: undefined}**
import React, { Component } from 'react'
import fetch from 'isomorphic-fetch';
import {connect} from 'react-redux';
import {loginUser} from '../../actions/LoginActions'
export class Login extends Component {
constructor(props) {
super(props)
this.state = {
email:"",
password:""
}
this.handleSubmit=this.handleSubmit.bind(this);
}
handleSubmit(event) {
console.log(this.state.email);
event.preventDefault();
fetch("http://127.0.0.1:3001/user/login",{
method:'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
UserEmail:this.state.email,
Password:this.state.password,
})},{withCredentials:'include'})
.then (res=>res.json())
.then (res=>{
if(res.message==='Authorized'){
console.log("authorized");
console.log(this.props);
let { email, password } = this.state;
**this.props.login(email,password);** //here I get the error
this.setState({
email : "",
password : ""
});
localStorage.setItem('sessionType', res.result.sessionType);
localStorage.setItem("UserId" , res.result.UserId);
}
else{
console.log("error");
}
})
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<formgroup>
<input
type="email"
value={this.state.email}
onChange={(event)=>{this.setState({ email: event.target.value })}}
placeholder="Email"
id="email"
required
/>
</formgroup>
<formgroup>
<input
type="password"
value={this.state.password}
type="password"
onChange={(event)=>{this.setState({ password: event.target.value })}}
placeholder="Password "
id="password"
required
/>
</formgroup>
<input type="submit" value="Submit" />
</form>
</div>
)
}
}
const mapDispatchToProps = (dispatch) => {
return({
login: (email,password) => {dispatch(loginUser(email,password))}
})
}
const mapStateToProps = (state) =>{
return{}
}
export default connect (mapStateToProps,mapDispatchToProps) (Login)
LoginUser Action file :
import * as actionType from './ActionType';
import fetch from 'isomorphic-fetch';
export const loginBegin =(isloginPending) =>({
type :actionType.LOGIN_BEGINS,
payload:isloginPending
});
export const login =(isloginSuccess) =>({
type :actionType.LOGIN_COMPLETE,
payload:isloginSuccess
});
export const loginError =(isloginError) =>({
type :actionType.LOGIN_ERROR,
payload:isloginError
});
export function loginUser(email, password) {
return dispatch => {
dispatch(loginBegin(true));
dispatch(login(false));
dispatch(loginError(null));
callLoginApi(email, password, error => {
dispatch(loginBegin(false));
if (!error) {
dispatch(login(true));
} else {
dispatch(loginError(error));
}
});
}
}
in mapDispatchToProps you return one object, have you tried removing the parentheses in the function?
const mapDispatchToProps = (dispatch) => {
return {
login: (email,password) => {dispatch(loginUser(email,password))}
}
}
mapDispatchToProps is supposed to be just an object.
const mapDispatchToProps = {
login: loginUser
}
loginUser is the action that you're importing to mapDispatchToProps and you're assinging that as this.props.login.

In React-Redux app, trying to pre-fill the default value in Edit Component with current api calls value

In my reactredux app, There is a peculiar situaton where I am currently trying to pre-fill my input field in Edit component but the thing is ,Its getting filled but not with current api calls but with last api calls that happens inside componentDidMount().I tried to clear the object too but all in vain. Kindly suggest
ProfileEdit.js component
import React, { Component } from 'react';
import '../App.css';
import {connect} from 'react-redux';
import {profileFetchDetail} from '../actions/profile';
import { withRouter } from 'react-router-dom';
class ProfileEdit extends Component {
constructor(props){
super(props);
this.state = {
firstName: '',
lastName: '',
emailId: '',
}
}
componentDidMount(){
const id = this.props.match.params.id;
this.props.profileFetchDetail(id);
this.setState({
firstName: this.props.profile.firstName,
lastName: this.props.profile.lastName,
emailId: this.props.profile.emailId
})
}
render() {
const {firstName,lastName,emailId} = this.state;
console.log(this.props.profile);
return (
<form name="profileCreate" className="profile-form">
<div className="form-control">
<label htmlFor="firstName">First Name</label><br/>
<input type="text" id="firstName" defaultValue={firstName}
name="firstName" placeholder="First Name"
/>
</div>
<div className="form-control">
<label htmlFor="LastName">Last Name</label><br/>
<input type="text" id="LastName" defaultValue={lastName}
name="lastName" placeholder="Last Name"
/>
</div>
<div className="form-control">
<label htmlFor="email">Email</label><br/>
<input type="email" id="email" defaultValue={emailId}
/>
</div>
<div className="form-action">
<input type="submit" value="Click here" />
</div>
</form>
)
}
}
const mapStateToProps = state => ({
profile: state.profile.profile
})
export default connect(mapStateToProps, {profileFetchDetail})(withRouter(ProfileEdit));
Action creators, here profileFetchDetail() is of our interest
import api from '../api';
// profile create
export const profileAdd = (formData, history) => async dispatch => {
console.log(formData);
const config = {
headers : { 'Content-Type': 'application/json' }
}
try {
await api.post('/api/profile/create', formData, config);
dispatch({ type: 'CREATE_PROFILE', payload: formData });
history.push('/list');
} catch (error) {
console.log(error);
}
}
// profile get all list
export const profileFetch = () => async dispatch => {
try {
const res = await api.get('/api/profile/list');
dispatch({ type: 'GET_PROFILE', payload: res.data });
} catch (error) {
console.log(error);
}
}
// profile get single list item corresponding to id
export const profileFetchDetail = (id) => async dispatch => {
dispatch({ type: 'CLEAR_PROFILE' });
try {
const res = await api.get(`/api/profile/${id}`);
dispatch({ type: 'GET_PROFILE_SINGLE', payload: res.data });
} catch (error) {
console.log(error);
}
}
// profile delete
export const profileDelete = (id) => async dispatch => {
dispatch({ type: 'CLEAR_PROFILE' });
try {
const res = await api.delete(`/api/profile/${id}/delete`);
dispatch({ type: 'DELETE_PROFILE', payload: res.data });
dispatch(profileFetch());
} catch (error) {
console.log(error);
}
}
ProfileReducers
const initialState = {
profiles:[],
profile:{}
};
export default (state = initialState, action) => {
switch (action.type) {
case 'CREATE_PROFILE':
return {...state, profiles: [...state.profiles, action.payload]};
case 'GET_PROFILE':
return {...state, profiles: action.payload};
case 'GET_PROFILE_SINGLE':
return {...state, profile: action.payload};
case 'CLEAR_PROFILE':
return {...state, profile: {}};
case 'DELETE_PROFILE':
return {...state, profiles: state.profiles.filter( item => item._id !== action.payload) };
default:
return state;
}
};
First time it loads perfectly on clicking edit button then the issue happens on clicking any other edit button.Pasting the example of 2 api calls inside componentDidMount().
In the attached image, the last api request in sequence displayed is the currently made request.Api made detail
Note: Till now I am not trying to edit it just prefilling data,where issue happening.

Redux Saga seems to be making an unknown API call?

I have a login form (built with Ant Design's Form). This is all hooked up to Redux Saga. In this Saga, I am making an API call, and everything seems to be working, but mysteriously, when I dispatch any other action using put, there seems to be an extra mysterious API call that fails because it's not reading from the form correctly.
My Form:
import React from 'react';
import { connect } from 'react-redux';
import { Form, Icon, Input, Button } from 'antd';
import { FormComponentProps } from 'antd/lib/form';
import { loginRequest, ICredentials } from '../redux/auth';
import { FormContainer, LoginContainer } from './styles';
type Props = {
login: (data: ICredentials) => {};
}
class LoginForm extends React.Component<Props & FormComponentProps> {
handleSubmit = (e: React.SyntheticEvent) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.login(values)
}
});
}
render() {
const { getFieldDecorator } = this.props.form;
return (
<FormContainer>
<Form className="login-form">
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</Form.Item>
<Form.Item>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</Form.Item>
<LoginContainer>
<Button type="primary" htmlType="submit" className="login-form-button" onClick={this.handleSubmit}>
Log in
</Button>
</LoginContainer>
</Form>
</FormContainer>
);
}
}
const mapDispatchToProps = { login: loginRequest };
const WrappedLoginForm = Form.create()(LoginForm);
export default connect(null, mapDispatchToProps)(WrappedLoginForm);
My Action:
export const loginRequest = (data: ICredentials): Action => ({
type: Actions.LoginRequest,
payload: data,
});
My Reducer:
const initialState: State = {
loading: false,
}
const reducer: Reducer<State> = (state = initialState, action: Actions & any) => {
switch (action.type) {
case Actions.LoginRequest:
return {
...state,
loading: true,
}
case Actions.LoginSuccess:
return {
...state,
loading: false,
}
default:
return state;
}
}
export default reducer;
my Sagas:
import { all, call, takeLatest, put } from 'redux-saga/effects';
import axios from 'axios';
import { push } from 'react-router-redux'
import message from 'antd/lib/message';
import { loginRequest, LoginRequest, loginSuccess } from './actions';
import { ICredentials } from './model';
import { AccessToken } from '../../storage/token';
const login = (payload: ICredentials) => axios({
method: 'POST',
url: //....
data: {
username: payload.username,
password: payload.password,
grant_type: 'password',
scope: 'admin,super_admin',
},
headers: {
'Content-Type': 'application/json',
Authorization: //....
}
});
function* loginSaga({ payload }: LoginRequest) {
try {
const data = yield call(login, payload);
AccessToken.set({ ...data.data, retrieved_at: Date.now() / 1000 })
yield call(message.success, 'Welcome!');
// yield all([
// put(loginSuccess()),
// put(push('/'))
// ]);
// yield put(loginSuccess());
// yield put(push('/'))
} catch (err) {
console.log(err)
}
}
function* watcherSaga() {
yield takeLatest(loginRequest, loginSaga)
}
export default watcherSaga;
In this saga, when the AccessToken is set, I ideally want to use react-router-redux to push the user into another route. However, it seems that there's a mysterious API call that's being made and it fails because there are no credentials being passed.
Pictured here is the API call coming back with a 200, but then comes back with a 400 because it's looking for username again
I suspect that it's the form that may be at fault, and though I don't want to switch to another form library, I feel like I may have to do that. Does anyone have any thoughts?
takeLatest ideally must be provided with a string. In your case a function is passed which returns an object.
takeLatest does not check the contents(i.e keys and values) of the object. That is something you would have to do on your own.
So, no matter what action is dispatched the login saga is started, which calls the login api, with an inappropriate payload and hence the error.
So, to avoid the error you could pass a string to takeLatest or in other words initiate the login saga only when Actions.LoginRequest(an action of type Actions.LoginRequest) is dispatched.
yield takeLatest(Actions.LoginRequest, loginSaga)

React Redux action payload data undefined

I'm trying to set up authentication for my app. Data is returned by axios and action payload is called correctly. The problem comes when I try to access the data contained in the payload. It returns undefined.
Sign in component with redux-form:
class Signin extends Component {
submit = values => {
this.props.signInAction(values, this.props.history);
};
errorMessage() {
if (this.props.errorMessage) {
return <div className="info-red">{this.props.errorMessage}</div>;
}
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.submit)} className="formu">
<div>
<div className="inputf">
<Field
name="login"
component="input"
type="text"
placeholder="Username"
/>
</div>
</div>
<div>
<div className="inputf">
<Field
name="password"
component="input"
type="password"
placeholder="Password"
/>
</div>
</div>
<div>
<button className="bsignin" type="submit">
Sign in
</button>
{this.errorMessage()}
</div>
</form>
);
}
}
function mapStateToProps(state) {
return { errorMessage: state.auth.error };
}
const reduxFormSignin = reduxForm({
form: "signin"
})(Signin);
export default connect(
mapStateToProps,
{ signInAction }
)(reduxFormSignin);
Action creator
export function signInAction({ login, password }, history) {
return async dispatch => {
try {
const res = await HTTP.post(`authenticate`, {
login,
password
});
localStorage.setItem("token", res.data.token);
const req = await HTTP.get("account");
dispatch({
type: AUTHENTICATED,
payload: req.data
});
history.push("/");
} catch (error) {
dispatch({
type: AUTHENTICATION_ERROR,
payload: "Invalid userName or password"
});
}
};
}
Reducer
import {
AUTHENTICATED,
UNAUTHENTICATED,
AUTHENTICATION_ERROR
} from "../actions";
const initialState = {
login: "",
authority: ""
};
export default function(state = initialState, action) {
switch (action.type) {
case AUTHENTICATED:
//This console log works and returns the data
console.log(action.payload);
//Next console log returns payload is undefined
//console.log(action.payload.login);
return {
...state,
authenticated: true,
// login: action.payload.login,
// authority: action.payload.authority
};
case UNAUTHENTICATED:
return { ...state, authenticated: false };
case AUTHENTICATION_ERROR:
return { ...state, error: action.payload };
default:
return state;
}
}
I'd like to set login and authority with the data comming from the payload but can't access the data inside it. ¿What am I missing?
Redux Form has an onSubmit function which takes in an action directly
https://redux-form.com/8.1.0/examples/remotesubmit/
<form onSubmit={handleSubmit} className="formu">
Then wrap within Redux Form
const reduxFormSignin = reduxForm({
form: "signin",
onSubmit: signInAction
})(Signin);
Check within the Redux Debugger, you should see redux form recording data
Also remember to pass form reducer to your store as stated here https://redux-form.com/8.1.0/docs/gettingstarted.md/
import { createStore, combineReducers } from 'redux'
import { reducer as formReducer } from 'redux-form'
const rootReducer = combineReducers({
// ...your other reducers here
// you have to pass formReducer under 'form' key,
// for custom keys look up the docs for 'getFormState'
form: formReducer`enter code here`
})

How can i access certain res.data from async action?

I am trying to access data received from web API to action in component. I set up registerUser action that posts new user data to API and then it is being sent to DB. API sents back status in JSON format. I want to render errors/notifications based on what was being passed as value of status key.
EDIT: I added key status in redux state, in REGISTER_USER type of action i am assigning value to it according to status being sent from backend.
However, i cannot access this propery in state by this.props.state/this.props.user - console loging it results in "undefined"
authActions.js
const authState = {
users: [],
status: ''
}
export const registerUser = user => dispatch => {
axios.post('https://damianlibrary.herokuapp.com/users/register', user)
.then(res => dispatch({
type: REGISTER_USER,
payload: res.data,
status: res.data.status
}))
}
authReducer.js
import { LOGIN_USER, REGISTER_USER } from '../actions/types';
const authState = {
users: []
}
export default function(state = authState, action) {
switch(action.type) {
case LOGIN_USER:
return {
...state
};
case REGISTER_USER:
return {
...state,
users: [action.payload, ...state.users]
};
default:
return state;
}
}
RegistrationForm.js component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { registerUser } from '../../actions/authActions';
import './RegisterForm.css';
class RegisterForm extends Component {
state = {
user_name: '',
password: '',
}
onChangeHandler = (e) => {
this.setState({ [e.target.name]: e.target.value })
};
onSubmitHandler = (e) => {
const { user_name, password } = this.state
const newUser = {
user_name: user_name,
password: password
}
this.props.registerUser(newUser)
this.setState({
user_name: '',
password: ''
})
e.preventDefault();
}
render() {
const { user_name, password } = this.state;
return (
<div className='formContainer'>
<div className='form'>
<form className='bookForm' onSubmit={this.onSubmitHandler.bind(this)}>
<div className='inputs'>
<input
type='text'
name='user_name'
placeholder='Username'
onChange={this.onChangeHandler}
value={user_name}/>
<input
type='password'
name='password'
placeholder='Password'
onChange={this.onChangeHandler}
value={password}/>
</div>
<div className='buttonSpace'>
<button>Register</button>
</div>
</form>
</div>
</div>
)
}
}
const mapStateToProps = (state) => ({
user: state.user
});
export default connect(mapStateToProps, { registerUser })(RegisterForm);
Do i have to get such value in my App container (It is in ), then get status: state.status (redux state) and pass it via props to my RegisterForm component?
store.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(rootReducer, initialState, compose(
applyMiddleware(...middleware)
));
export default store;
rootReducer.js
import { combineReducers } from 'redux';
import bookReducer from './bookReducer';
import authReducer from './authReducer';
export default combineReducers({
book: bookReducer,
auth: authReducer
});
Fixed my issue. I called auth: authReducer in my rootReducer.js file and after that i tried to get what my reducer was returning by calling user: state.user instead of user: state.auth.
I can reach my redux state without any problems now.

Resources