reactjs payload shows undefined - reactjs

I am kind of learning how react js and its workflows such as redux work out.. In some cases I have no idea why I can not get response from API such as below my code when I request data I can nott get payload it is undefined any suggestion ? and may I know what cause it is show that payload is undefined.
RegisterAction.js
import { REGISTER_REQUEST, REGISTER_SUCCESS, REGISTER_FAILURE } from './types';
import { authService } from '../services/authService';
export function register(user) {
return dispatch => {
dispatch(request(user));
authService.register(user)
.then(
user => { dispatch(success()); },
error => { dispatch(failure(error)); }
);
};
function request(user) {
return { type: REGISTER_REQUEST, payload: user }
}
function success(user) {
return { type: REGISTER_SUCCESS, payload: user }
}
function failure(error) {
return { type: REGISTER_FAILURE, error }
}
}
RegistrationReducer.js
import { REGISTER_REQUEST, REGISTER_SUCCESS, REGISTER_FAILURE } from '../actions/types';
const initialState = {
user: [],
registerigIn: false,
registeredIn: false
}
export default function (state = initialState, action) {
console.info("reducer:", "state is:", state, "action is:", action.payload)
switch (action.type) {
case REGISTER_REQUEST:
return {
...state,
user: action.payload,
registering: true,
};
case REGISTER_SUCCESS:
return {
...state,
user: action.payload,
registered: true
};
case REGISTER_FAILURE:
return {
registering: false
};
default:
return state
}
}
AuthService.js
import { authHeader } from '../helpers/authHeader';
export const authService = {
register
};
function register(user) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
};
return fetch(`https://reqres.in/api/register`, requestOptions).then(handleResponse);
}
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
if (response.status === 401) {
// auto logout if 401 response returned from api
logout();
// location.reload(true);
}
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
return data;
});
}
View
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { register } from '../../actions/registrationActions'
import './Authentication.css'
class Registration extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: ''
}
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this)
}
onChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
onSubmit(e) {
e.preventDefault();
const user = this.state;
console.info("register Data:", user)
this.props.register(user)
}
render() {
return (
<React.Fragment>
< div className="text-center" >
<div className="logo">Register Page</div>
<div className="login-form-1">
<form id="register-form" className="text-left" onSubmit={this.onSubmit}>
<div className="login-form-main-message"></div>
<div className="main-login-form">
<div className="login-group">
<div className="form-group">
<label htmlFor="reg_surname" className="sr-only">Email</label>
<input
type="text"
className="form-control"
id="email"
name="email"
onChange={this.onChange}
placeholder="email" />
</div>
<div className="form-group">
<label htmlFor="reg_password" className="sr-only">Password</label>
<input type="password"
className="form-control"
id="password"
name="password"
onChange={this.onChange}
placeholder="" />
</div>
</div>
<button
type="submit"
title="Gönder"
className="login-button"><i className="fa fa-chevron-right"></i>
</button>
</div>
<div className="etc-login-form">
<p>Hesabiniz var mı? Giriş yap</p>
</div>
</form>
</div>
</div >
</React.Fragment>
)
}
}
Registration.propTypes = {
register: PropTypes.func.isRequired
}
export default connect(null, { register })(Registration)

Related

My this.props are undefined on handleSubmit and other function

I do not know why my this.props are showing undefined on handleSubmit. I have done everything possible. When I log state in mapStateToProps, I see what the action has dispatched, but this.props is not taking it.
This is what I get (https://imgur.com/a/AXixWn9) in logger when I enter the wrong details
I have searched online but no clue. I have debugged this for hours
Login.js
import React from 'react';
import { NavLink } from 'react-router-dom';
import { Redirect } from 'react-router';
import qs from 'qs';
import { connect } from 'react-redux';
import Outer from '../templates/outer';
import history from '../../_helpers/history';
import routes from '../../services/urls';
import apiRequest from '../../services/api';
import loginUser from '../../redux/actions/authentication.action';
import { LOGIN_FAILURE, LOGIN_SUCCESS } from '../../redux/constants/authentication.constants';
import alertActions from '../../redux/actions/alert.actions';
class LoginComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
fields: {},
errors: {},
loginError: '',
submitted: false,
};
const { dispatch } = this.props;
dispatch(alertActions.clear());
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(field, e) {
const { fields } = this.state;
fields[field] = e.target.value;
this.setState({ fields });
}
handleValidation() {
const { fields } = this.state;
const errors = {};
let formIsValid = true;
// Login code
if (!fields.code) {
formIsValid = false;
errors.code = 'This field is required';
}
// Password
if (!fields.password) {
formIsValid = false;
errors.password = 'This field is required';
}
this.setState({ errors });
return formIsValid;
}
handleSubmit(e) {
e.preventDefault();
const { code, password } = this.state.fields;
this.setState({ submitted: true });
if (this.handleValidation()) {
this.setState({ submitted: true });
const payload = {
email: code,
// level: 'SYSTEM',
password,
};
const { dispatch } = this.props;
dispatch(loginUser(payload));
} else {
this.setState({ submitted: false });
}
}
render() {
const { loginError, submitted } = this.state;
return (
<Outer>
<form onSubmit={this.handleSubmit}>
<div className="mt-5" />
{loginError
&& <div className="alert alert-danger">{loginError}</div>
}
<label>Login Code</label>
<input type="text" className="form-control" onChange={this.handleChange.bind(this, 'code')} value={this.state.fields.code || ''} />
<div className="text-danger mt-1 mb-1">{this.state.errors.code}</div>
<br />
<label>Password</label>
<input type="password" className="form-control" onChange={this.handleChange.bind(this, 'password')} value={this.state.fields.password || ''} />
<div className="text-danger mt-1 mb-1">{this.state.errors.password}</div>
<br />
<button
className="btn btn-primary btn-block text-uppercase"
type="submit"
disabled={submitted}
>
{ !submitted ? 'Login to manage my cards' : 'loading...' }
</button>
<div className="row no-gutters mt-4">
<div className="col-md-12">
<NavLink to="/reset-password" className="grey">I have forgotten my password</NavLink>
</div>
</div>
</form>
</Outer>
);
}
}
function mapStateToProps(state) {
console.error(state);
const { auth } = state.authentication;
const { alert } = state.alert;
return { auth, alert };
}
export default connect(mapStateToProps)(LoginComponent);
store.js
export const store = createStore(
rootReducer,
applyMiddleware(
thunkMiddleware,
loggerMiddleware,
),
);
authentication.reducer.js
function loginReducer(state = {}, action) {
switch (action.type) {
case LOGIN_REQUEST:
return {
user_status: LOGIN_REQUEST,
user_data: action,
};
case LOGIN_SUCCESS:
return {
user_status: LOGIN_SUCCESS,
user_data: action,
};
case LOGIN_FAILURE:
return {
user_status: LOGIN_FAILURE,
user_data: action,
};
default:
return state;
}
}
export default loginReducer;
authentication.action.js
function loginUser(payload) {
function request(user) { return { type: LOGIN_REQUEST, user }; }
function success(response) { return { type: LOGIN_SUCCESS, response }; }
function failure(error) { return { type: LOGIN_FAILURE, error }; }
return (dispatch) => {
// const request = apiRequest(routes.LOGIN, 'POST', qs.stringify(payload));
// const ah = loginUser(qs.stringify(payload));
// console.log(ah);
dispatch(request(payload));
const fetch = apiRequest(routes.LOGIN, 'POST', qs.stringify(payload));
return fetch.then((response) => {
switch (response.status) {
case 400:
dispatch(failure(response));
dispatch(alertActions.error(response.data.description));
break;
case 200:
if (response.data.code === 0) {
localStorage.setItem('qwplx44', JSON.stringify(response.data));
dispatch(success(response.data));
history.push('/dashboard');
break;
}
dispatch(failure(response.data.description));
dispatch(alertActions.error(response.data.description));
break;
default:
return {};
}
}).catch((error) => {
dispatch(failure(error.response.data.message));
dispatch(alertActions.error(error.response.data.message.toString()));
// return false;
});
root-reducer
const rootReducer = combineReducers({
authentication: loginReducer,
alert: alertReducer,
});
export default rootReducer;
You aren't connecting your component correctly. You are trying to destruct keys out of the state that aren't there. I don't see a key called auth on your loginReducer. So this line const { auth } = state.authentication; would return undefined. That is why when logging props auth is undefined.
Instead just pluck what you want from state and you can alias authentication as auth while destructuring :)
const mapStateToProps = ({authentication: auth, alert}) => ({
auth,
alert
})
If you are trying to use the data stored in your reducer after making the request, then you should use the componentDidUpdate lifecycle method
componentDidUpdate(prevProps) {
const { auth: prevAuth } = prevProps
const { auth } = this.props
if (auth.user_status && ((!prevAuth.user_status && auth.user_status) || prevAuth.user_status !== auth.user_status)) {
// TODO handle response from api here
/* ex
if (auth.user_status === LOGIN_FAILURE) {
this.setState({loginFailure: auth.user_data})
}
*/
}
}
Few other things.
You aren't binding handleValidation to the class, but are trying to access state.
You are calling this.setState({ submitted: true }); twice in handleSubmit. Second one is redundant and not needed.
I would refactor your apiRequest to handle the qs.stringify and the error / auth / response status handling, so you dont have to write out that same stuff for every api call :)

Cannot read property success of undefined using Reactjs Toastr

Am working with react toastr from this link https://www.npmjs.com/package/react-toastr for my react-redux form.
i have installed as npm install --save react-toastr and I have made import as per requirements
import { ToastContainer } from "react-toastr";
import { ToastMessage } from "react-toastr";
import { ToastMessageAnimated } from "react-toastr";
let container;
at src/index.html i have toastr.min.css and toastr.animate.css files
At RegisterPage Component when I click at Test Toastr Button as per code below, everything works fine
<div className="container">
<ToastContainer
ref={ref => container = ref}
className="toast-top-right"
/>
<h1>
React-Toastr
<small>React.js toastr component</small>
</h1>
<div className="btn-container">
<button
className="primary"
onClick={() =>
container.success(`hi! Now is `, `success`, {
closeButton: true,})
}
>
Test Toastr
</button>
</div>
</div>
Here is what am trying to do.
I want to display toastr message alert once form registeration is successful but it shows error
bundle.js:36689 Uncaught TypeError: Cannot read property 'success' of undefined when I added this code below at user.service.js files
setTimeout(()=>{ this.container.success(`hi! jmarkatti `, `success`, {closeButton: true,}); }, 400);
Here is the user.service.js files
import config from 'config';
import { authHeader } from '../_helpers';
import { ToastContainer } from "react-toastr";
import { ToastMessage } from "react-toastr";
import { ToastMessageAnimated } from "react-toastr";
let container;
export const userService = {
register,
update,
};
function register(user) {
const requestOptions = {
method: 'POST',
return fetch(`../register.php`, requestOptions).then(handleResponse)
.then(res => {
if (res) {
//setTimeout(()=>{ this.container.success('Data added successfully'); }, 400);
setTimeout(()=>{ this.container.success(`hi! jmarkatti `, `success`, {closeButton: true,}); }, 400);
console.log('Data added suucessfully');
}
return user;
});
}
The code below is the RegisterPage Component.
import React from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { userActions } from '../_actions';
import { ToastContainer } from "react-toastr";
import { ToastMessage } from "react-toastr";
import { ToastMessageAnimated } from "react-toastr";
let container;
class RegisterPage extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {
firstName: '',
lastName: '',
username: '',
password: ''
},
submitted: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const { name, value } = event.target;
const { user } = this.state;
this.setState({
user: {
...user,
[name]: value
}
});
}
handleSubmit(event) {
event.preventDefault();
this.setState({ submitted: true });
const { user } = this.state;
const { dispatch } = this.props;
if (user.firstName && user.lastName && user.username && user.password) {
dispatch(userActions.register(user));
}
}
render() {
const { registering } = this.props;
const { user, submitted } = this.state;
return (
<div className="col-md-6 col-md-offset-3">
<h2>Test Toastr</h2>
<div className="container">
<ToastContainer
ref={ref => container = ref}
className="toast-top-right"
/>
<h1>
React-Toastr
<small>React.js toastr component</small>
</h1>
<div className="btn-container">
<button
className="primary"
onClick={() =>
container.success(`hi! Jmarkatti `, `success`, {
closeButton: true,})
}
>
Test Toastr
</button>
</div>
</div>
<form name="form" onSubmit={this.handleSubmit}>
// form input removed to reduce code
<div className="form-group">
<button className="btn btn-primary">Register</button>
{registering &&
}
</div>
</form>
</div>
);
}
}
function mapStateToProps(state) {
const { registering } = state.registration;
return {
registering
};
}
const connectedRegisterPage = connect(mapStateToProps)(RegisterPage);
export { connectedRegisterPage as RegisterPage };
An Updates is Here
user.action.js code
function register(user) {
return dispatch => {
dispatch(request(user));
userService.register(user)
.then(
user => {
/*
dispatch(success());
history.push('/login');
dispatch(alertActions.success('Registration successful'));
*/
},
error => {
dispatch(failure(error.toString()));
dispatch(alertActions.error(error.toString()));
}
);
};
user.reducer.js codes
import { userConstants } from '../_constants';
export function registration(state = {}, action) {
switch (action.type) {
case userConstants.REGISTER_REQUEST:
return { registering: true };
case userConstants.REGISTER_SUCCESS:
return {};
case userConstants.REGISTER_FAILURE:
return {};
default:
return state
}
}
Try with this
Change
setTimeout(()=>{ this.container.success(`hi! jmarkatti `, `success`, {closeButton: true,}); }, 400);
To
setTimeout(()=>{
if(this.container){
this.container.success(`hi! jmarkatti `, `success`, {closeButton: true,});
}
}, 400);

How redirect to component when user login [duplicate]

I'm building react application using react-boilerplate. I have created authentication containers using react-boilerplate generator features and combine it with this public api . It will return token after we passed email & password.
How to redirect to other component / page after set the token to browser localStorage ?
Thank you for your time.
reducers.js
import { fromJS } from 'immutable';
import {
CHANGE_EMAIL,CHANGE_PASSWORD,LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE
} from './constants';
const initialState = fromJS({
email: '',
password: '',
isFetching: false,
isAuthenticated: false,
errorMessage: '',
});
function loginReducer(state = initialState, action) {
switch (action.type) {
case CHANGE_EMAIL:
return state
.set('email', action.email);
case CHANGE_PASSWORD:
return state
.set('password', action.password);
case LOGIN_REQUEST:
return state
.set('isFetching', true)
.set('isAuthenticated', false)
.set('user', action.creds);
case LOGIN_SUCCESS:
return state
.set('isFetching', false)
.set('isAuthenticated', true)
.set('errorMessage', '');
case LOGIN_FAILURE:
return state
.set('isFetching', false)
.set('isAuthenticated', false)
.set('errorMessage', action.message);
default:
return state;
}
}
export default loginReducer;
saga.js
import { call, put, select, takeLatest, takeEvery } from 'redux-saga/effects';
import { withRouter } from 'react-router-dom';
import { LOGIN_REQUEST } from './constants';
import { loginError, receiveLogin } from './actions';
import { postUserLogin } from './api';
import { makeSelectEmail, makeSelectPassword } from './selectors';
export function* loginUser() {
const requestURL = 'https://reqres.in/api/login';
try {
const email = yield select(makeSelectEmail());
const password = yield select(makeSelectPassword());
const creds = {
email: email,
password: password,
}
// test post
const headers = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: email,
password: password,
}),
};
const users = yield call(postUserLogin,creds);
const raw_token = JSON.stringify(users.token);
const token_id = JSON.parse(raw_token);
console.log("token_id : "+token_id);
yield put(receiveLogin(token_id));
localStorage.setItem('access_token', token_id);
} catch (err) {
console.log(err);
let error;
try {
error = yield err.response.json();
} catch (e) {
error = { errors: [{ detail: `${err.name}: ${err.message}` }] };
}
yield put(loginError(error.error));
}
}
// Individual exports for testing
export default function* defaultSaga() {
// See example in containers/HomePage/saga.js
yield takeEvery(LOGIN_REQUEST, loginUser);
}
index.js
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import { FormattedMessage } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
import { Button, FormGroup, FormControl, ControlLabel, Col, PageHeader, HelpBlock } from "react-bootstrap";
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';
import { makeSelectLogin,makeSelectEmail, makeSelectPassword, makeSelectErrorMessage, makeSelectIsAuthenticated } from './selectors';
import reducer from './reducer';
import saga from './saga';
import messages from './messages';
import { changeEmail, changePassword, requestLogin } from './actions';
export class Login extends React.Component { // eslint-disable-line react/prefer-stateless-function
componentDidMount() {
if ((this.props.email && this.props.email.trim().length > 0) && (this.props.password && this.props.password.length > 0) ) {
this.props.onSubmitForm();
}
}
renderErrorMessage(){
if(this.props.errorMessage){
return(
<div className="info-red">
{this.props.errorMessage}
</div>
);
}
}
render() {
return (
<div>
<Helmet>
<title>Login</title>
<meta name="description" content="Description of TestPage" />
</Helmet>
<div className="container-fluid">
<div className="row">
<div className="col-md-6 col-md-offset-3">
<div className="login-form">
<form name="loginForm" onSubmit={this.props.onSubmitForm}>
<div className="form-group">
<label htmlFor="email">Email address</label>
<input type='email'
id="email"
className="form-control"
placeholder='Email Address'
required autoFocus
value={this.props.email}
onChange={this.props.onChangeEmail}/>
<FormControl.Feedback />
<HelpBlock>Email cannot be blank</HelpBlock>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input id="password"
type="password"
className="form-control"
placeholder="Password"
required
value={this.props.password}
onChange={this.props.onChangePassword}/>
<FormControl.Feedback />
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary">Submit</button>
</div>
</form>
{this.renderErrorMessage()}
</div>
</div>
</div>
</div>
</div>
);
}
}
Login.propTypes = {
email: PropTypes.string,
password: PropTypes.string,
errorMessage: PropTypes.string,
isAuthenticated: PropTypes.bool.isRequired,
onChangeEmail: PropTypes.func,
onChangePassword: PropTypes.func,
onSubmitForm: PropTypes.func,
};
const mapStateToProps = createStructuredSelector({
login: makeSelectLogin(),
email: makeSelectEmail(),
password: makeSelectPassword(),
errorMessage: makeSelectErrorMessage(),
isAuthenticated: makeSelectIsAuthenticated(),
});
function mapDispatchToProps(dispatch) {
return {
onChangeEmail: (evt) => dispatch(changeEmail(evt.target.value)),
onChangePassword: (evt) => dispatch(changePassword(evt.target.value)),
onSubmitForm: (evt) => {
if (evt !== undefined && evt.preventDefault) evt.preventDefault();
const creds = {
email: email.value,
password: password.value,
}
dispatch(requestLogin(creds));
},
fetchPost: (evt) => {
dispatch(fetchPosts());
},
};
}
const withConnect = connect(mapStateToProps, mapDispatchToProps);
const withReducer = injectReducer({ key: 'login', reducer });
const withSaga = injectSaga({ key: 'login', saga });
export default compose(
withReducer,
withSaga,
withConnect,
)(Login);
action.js
import {
CHANGE_EMAIL,CHANGE_PASSWORD,LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE
} from './constants';
export function changeEmail(email) {
return {
type: CHANGE_EMAIL,
email,
};
}
export function changePassword(password) {
return {
type: CHANGE_PASSWORD,
password,
};
}
export function requestLogin(creds) {
return {
type: LOGIN_REQUEST,
isFetching: true,
isAuthenticated: false,
creds,
};
}
export function receiveLogin(user_token) {
return {
type: LOGIN_SUCCESS,
isFetching: false,
isAuthenticated: true,
token_id : user_token,
};
}
export function loginError(message) {
return {
type: LOGIN_FAILURE,
isFetching: false,
isAuthenticated: false,
message
}
}
See <Redirect/> for more info.
// Render.
render() {
// Is Authenticated.
if (this.props.isAuthenticated) return <Redirect to="/authpath"/>
// Is Not Authenticated.
// ..
}

Unable to edit form input using redux

I've created a container called siteEdit.js. It handles creating & editing "sites".
I've setup actionCreators that handles taking the form data and submitting it to the API. This works perfectly.
But when you visit the container using a route that contains an ID it will run an actionCreator that will fetch for the "Site" data based on the id param.
This all works as expected, but since I'm using redux, I'm setting the Input value with the props. for example, this.props.title
I'm trying to stay away from using the redux-form package for now.
Container:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {createSite, getSite} from '../../actions/siteActions';
class SiteEdit extends Component {
constructor(props) {
super(props)
this.state = {
title: '',
url: '',
description: '',
approvedUsers: []
}
this.handleSubmit = this.handleSubmit.bind(this)
this.handleInputChange = this.handleInputChange.bind(this)
}
componentWillMount() {
if(this.props.params.id) {
this.props.dispatch(getSite(this.props.params.id))
}
}
handleInputChange(e) {
const target = e.target
const value = target.type === 'checkbox' ? target.checked : target.value
const name = target.name
this.setState({
[name]: value
})
}
handleSubmit(e) {
e.preventDefault()
this.props.dispatch(createSite(this.state))
}
render() {
const {title, url, description, approvedUsers} = this.props
return (
<div className="SiteEdit">
<h1>NEW SITE</h1>
<form onSubmit={this.handleSubmit}>
<div className="block">
<label>Site Name</label>
<input
className="input"
type="text"
value={title ? title : this.state.title}
onChange={this.handleInputChange}
name="title" />
</div>
<div className="block">
<label>Site URL</label>
<input
className="input"
type="text"
value={this.state.url}
onChange={this.handleInputChange}
name="url" />
</div>
<div className="block">
<label>Description</label>
<input
className="textarea"
type="textarea"
value={this.state.description}
onChange={this.handleInputChange}
name="description" />
</div>
<div className="block">
<label>Approved Users</label>
<input
className="input"
type="text"
value={this.state.approvedUsers}
onChange={this.handleInputChange}
name="approvedUsers" />
</div>
<button className="button--action">Create</button>
</form>
</div>
)
}
}
const mapStateToProps = (state) => ({
title: state.sites.showSite.title,
url: state.sites.showSite.url,
description: state.sites.showSite.description,
approvedUsers: state.sites.showSite.approvedUsers
})
SiteEdit = connect(mapStateToProps)(SiteEdit)
export default SiteEdit
ActionCreators:
import config from '../config'
import { push } from 'react-router-redux'
const apiUrl = config.api.url
// List all sites
export const LIST_SITES_START = 'LIST_SITES_START'
export const LIST_SITES_SUCCESS = 'LIST_SITES_SUCCES'
export const LIST_SITES_ERROR = 'LIST_SITES_ERROR'
export function sitesListStart(data) {
return { type: LIST_SITES_START, data }
}
export function sitesListSuccess(data) {
return { type: LIST_SITES_SUCCESS, data }
}
export function sitesListError(data) {
return { type: LIST_SITES_ERROR, data }
}
export function listSites() {
return (dispatch) => {
dispatch(sitesListStart())
fetch(`${apiUrl}/listSites`)
.then(res => res.json())
.then(json => {
dispatch(sitesListSuccess(json))
})
.catch(error => {
dispatch(sitesListError)
})
}
}
// Create & Edit Sites
export const CREATE_SITE_START = 'CREATE_SITE_START'
export const CREATE_SITE_SUCESS = 'CREATE_SITE_SUCCESS'
export const CREATE_SITE_ERROR = 'CREATE_SITE_ERROR'
export function siteCreateStart(data) {
return { type: CREATE_SITE_START, data}
}
export function siteCreateSuccess(data) {
return { type: CREATE_SITE_SUCCESS, data}
}
export function siteCreateError(error) {
return { type: CREATE_SITE_ERROR, error}
}
export function createSite(data) {
return (dispatch) => {
dispatch(siteCreateStart())
fetch(`${apiUrl}/createSite`, {
method: 'post',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(json => {
dispatch(push('/'))
dispatch(siteCreateSuccess())
})
.catch(error => {
dispatch(siteCreateError())
})
}
}
// Get Single Site
export const GET_SITE_START = 'GET_SITE_START'
export const GET_SITE_SUCCESS = 'GET_SITE_SUCCESS'
export const GET_SITE_ERROR = 'GET_SITE_ERROR'
export function getSiteStart(data) {
return { type: GET_SITE_START, data}
}
export function getSiteSuccess(data) {
return { type: GET_SITE_SUCCESS, data}
}
export function getSiteError(error) {
return { type: GET_SITE_ERROR, error}
}
export function getSite(id) {
return (dispatch) => {
dispatch(getSiteStart())
fetch(`${apiUrl}/getSite/${id}`)
.then(res => res.json())
.then(json => {
dispatch(getSiteSuccess(json))
})
.catch(error => {
dispatch(getSiteError())
})
}
}
Reducers:
import {push} from 'react-router-redux'
import {
LIST_SITES_START,
LIST_SITES_SUCCESS,
LIST_SITES_ERROR,
GET_SITE_START,
GET_SITE_SUCCESS,
GET_SITE_ERROR
} from '../actions/siteActions'
const initialState = {
sitesList: {
sites: [],
error: null,
loading: true
},
showSite: {
title: '',
url: '',
description: '',
approvedUsers: [],
loading: true
}
}
export default function (state = initialState, action) {
switch (action.type) {
// List Sites
case LIST_SITES_START:
return Object.assign({}, state, {
sitesList: Object.assign({}, state.sitesList, {
loading: true
})
})
case LIST_SITES_SUCCESS:
return Object.assign({}, state, {
sitesList: Object.assign({}, state.sitesList, {
sites: action.data,
loading: false
})
})
case LIST_SITES_ERROR:
return Object.assign({}, state, {
error: action.error,
loading: false
})
case GET_SITE_START:
return Object.assign({}, state, {
showSite: Object.assign({}, state.showSite, {
loading: true
})
})
case GET_SITE_SUCCESS:
return Object.assign({}, state, {
showSite: Object.assign({}, state.showSite, {
...action.data,
loading: false
})
})
case GET_SITE_ERROR:
return Object.assign({}, state, {
showSite: Object.assign({}, state.showSite, {
error: action.error,
loading: false
})
})
default:
return state
}
}
You are setting the ternary for the value with the props.title taking precedent, like so just to re-iterate -
const { title } = this.props;
...
value={title ? title : this.state.title}
Your onChange logic is correct and is probably updating your components local state correctly, however you still have this.props.title, so it will take precedent in that ternary.
There are a bunch of ways you could handle this, it will depend on order of operations really (that is when props.title will be truthy or not). Assuming you have the title when the component mounts you can do something in the constructor like:
constructor(props) {
super(props)
this.state = {
title: props.title, << set default here
url: '',
description: '',
approvedUsers: []
}
then in the input you only need to set the value to the state title
value={this.state.title}
This will depend on when the props.title value comes into your component of course, if it is not there for mount, this will not work as intended.
You could also pass a function to evaluate all of this for the value of the input as well - inside of which you would more verbosely check the props.title vs the state.title and decide which one to return as your value.
<input value={this.returnTitleValue} .. << something like so
Hope this helps!

How to pass my error message from server called by axios to my component

I'm really new to React. I have an axios request in my actions I want my error message to pass on the component I have this code :
import axios from 'axios';
import setAuthorizationToken from '../utils/setAuthorizationToken';
import jwtDecode from 'jwt-decode';
import { SET_CURRENT_USER, BASE_URL } from './types';
const instance = axios.create({
baseURL: BASE_URL
});
export function setCurrentUser(user) {
return {
type: SET_CURRENT_USER,
user
};
}
export function logout() {
return dispatch => {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
setAuthorizationToken(false);
dispatch(setCurrentUser({}));
}
}
export function login(data) {
return dispatch => {
return instance.post('/authenticate', data).then(function(response) {
const token = response.data.accessToken;
const refreshToken = response.data.refreshToken;
localStorage.setItem('accessToken', token);
localStorage.setItem('refreshToken', refreshToken);
setAuthorizationToken(token);
dispatch(setCurrentUser(jwtDecode(token)));
})
.catch(function(error){
console.log('error: ', error.response.data);
});
}
}
Here is my Component:
import React from 'react';
import TextFieldGroup from '../common/TextFieldGroup';
import validateInput from '../../server/validations/login';
import { connect } from 'react-redux';
import { login } from '../../actions/authActions';
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
errors: {},
isLoading: false
};
this.onSubmit = this.onSubmit.bind(this);
this.onChange = this.onChange.bind(this);
}
isValid() {
const { errors, isValid } = validateInput(this.state);
if (!isValid) {
this.setState({ errors });
}
return isValid;
}
onSubmit(e) {
e.preventDefault();
if (this.isValid()) {
this.setState({ errors: {}, isLoading: true });
this.props.login(this.state).then(
(res) => this.context.router.push('/'),
(error) => this.setState({ errors: error.response.data , isLoading: false }),
);
}
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
render() {
const { errors, username, password, isLoading } = this.state;
return (
<form onSubmit={this.onSubmit}>
<h1>Login</h1>
{ errors.message && <div className="alert alert-danger">{errors.message}</div> }
<TextFieldGroup
field="username"
label="Username"
value={username}
error={errors.username}
onChange={this.onChange}
/>
<TextFieldGroup
field="password"
label="Password"
value={password}
error={errors.password}
onChange={this.onChange}
type="password"
/>
<div className="form-group"><button className="btn btn-primary btn-lg" disabled={isLoading}>Login</button></div>
</form>
);
}
}
LoginForm.propTypes = {
login: React.PropTypes.func.isRequired
}
LoginForm.contextTypes = {
router: React.PropTypes.object.isRequired
}
export default connect(null, { login })(LoginForm);
Here is the console.log
error: Object {code: "UNAUTHORIZED", message: "Invalid username or password."}
Currently I don't know to pass my error message to component. I'm really new to React and Redux
First you have to add the initial state on reducer. For example
authReducer.js
const initialState = {
... // another state
errors: {}
}
function yourReducer(state = initialState, action) {
case 'SHOW_ERROR':
return {
...state,
errors: action.message
}
default:
return state
}
On login action dispatch the 'SHOW_ERROR'
authActions.js
export function login(data) {
return dispatch => {
return instance.post('/authenticate', data).then(function(response) {
...
// success
})
.catch(function(error){
// fail
dispatch({ type: 'SHOW_ERROR', message: error.response.data })
});
}
}
Then you need to map redux state to be a props on your component
LoginComponent.js
function mapStateToProps(state) {
return {
you: may.return.another.state.here,
errors: state.yourReducerName.errors
}
}
export default connect(mapStateToProps, { login })(LoginForm);
Finally, you can call errors as a props on your Component
class LoginForm extends React.Component {
...
render() {
const { errors, username, password, isLoading } = this.state;
const { errors } = this.props // errors from redux state
return (
<form onSubmit={this.onSubmit}>
<p>{errors.message}</p>
<h1>Login</h1>
...
<div className="form-group"><button className="btn btn-primary btn-lg" disabled={isLoading}>Login</button></div>
</form>
);
}
}
Don't forget to validate the prop types. Good luck!

Resources