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);
Related
I have created one universal custom spinner/loader hook for my react application , I want to use that loader inside my component where I used to invoke API calls, the problem is the API calls are written inside the class components and as per the react doc we cannot use hooks inside the class component, most of the API calls written inside the class component.
I just want to know is there anyway to achieve the same, as the loader I have created is class based, but I then created a hook for the usage.
LoaderComponent
import React, {Component} from 'react';
import { Spin } from 'antd';
import 'antd/dist/antd.css';
import { LoadingOutlined } from '#ant-design/icons';
export default class LoaderComponent extends React.PureComponent {
render(){
const antIcon = <LoadingOutlined style={{ fontSize: 24 }} spin />;
return (
<div className="fp-container">
<Spin indicator={antIcon}className="fp-loader" alt="loading" />;
</div>)
}
}
useLoader
import React from 'react'
import LoaderComponent from '../index'
export const useLoader = () => {
const[initLoad, setInitLoad]=useState(false)
return [ initLoad ? <LoaderComponent /> : null,
() => setInitLoad(true), //Show loader
() => setInitLoad(false) //Hide Loader
]
}
Component
The below is the component where I want to use the loader, there is two API calls implemneted into the same. I have tried to use the same but not suceeded.
import React, { Component } from 'react';
import { Row, Col , notification} from 'antd';
import WaitingForCallComponent from '#components/WaitingForCallComponent';
import { connect } from 'react-redux';
import { SET_AGENT_DETAILS } from '#actions';
import { SET_CONFIG_SERVER, GET_REGISTER_AGENT } from "#Utils/Urls";
import makeRequest from '#Utils/MakeRequest';
import { sessionService } from "redux-react-session";
import socketConnection from '#Hoc/SocketComponent';
import useLoader from '#Hoc/LoaderComponent/hook';
export class WaitingContainer extends Component {
constructor(props) {
super(props);
this.state = {
extensionNo: "",
agentId: "",
genesysId: "",
username: "",
agentStatus:"",
};
}
componentDidMount = () => {
window.history.pushState(null, document.title, window.location.href);
window.addEventListener('popstate', this.callWindow)
sessionService.loadUser().then((currentUser) => {
this.setState({
username: currentUser.name,
agentId: currentUser.params.AgentID,
genesysId: currentUser.params.genesysID,
}
, () => {
this.setConfig();
});
});
};
callWindow =()=>{
window.history.pushState(null, document.title, window.location.href);
}
handleException = (e) => {
notification.error({
message: 'Agent Registration Error',
description: e?.data?.description,
duration: 0
});
this.setState({
spinLoader: false
});
};
available = (extensionNo, agentId, genesysId) => {
makeRequest
.postAuth(GET_REGISTER_AGENT, {
data: {
extensionNo: extensionNo,
agentId: agentId,
reason: 'unknown',
agentStatus: 'ready',
genesysId: genesysId
}
})
.then((response) => {
if (response && response.data && !response.data.error) {
if (response.data.data.phoneStatus) {
this.props.setExtension({
agentStatus: response.data.data.agentStatus??'ready',
agentSessionId: response.data.data.agentSessionId,
extensionNo: extensionNo,
agentId: agentId,
genesysId: genesysId
});
this.setState({
agentStatus:response.data.data.agentStatus??'ready'
})
setTimeout(() => {
sessionService.loadUser().then((currentUser) => {
if (!currentUser.extraDetails) {
currentUser.extraDetails = {};
}
currentUser.extraDetails.agentStatus = response.data.data.agentStatus;
currentUser.extraDetails.agentSessionId = response.data.data.agentSessionId;
currentUser.extraDetails.extensionNo = extensionNo;
sessionService.saveUser(currentUser).then(() => {
socketConnection(this.props);
});
});
}, 1000);
} else {
this.handleException({
data: {
description: 'Please login into softphone extension ' + extensionNo
}
});
}
} else {
this.handleException(response);
}
})
.catch(this.handleException);
};
setConfig = () => {
sessionService.loadUser().then((currentUser) => {
makeRequest
.postAuth(SET_CONFIG_SERVER, {
data: {
username: currentUser?.params?.username,
},
})
.then((response) => {
if (response?.data?.data.extensionNo ?? false) {
this.setState({
extensionNo: response?.data?.data.extensionNo ?? "",
}, () => {
this.available(this.state.extensionNo, this.state.agentId, this.state.genesysId);
notification.success({
type: "success",
message: "Extension Number",
description: "Extension Verified",
});
})
} else {
notification.error({ type: "error", message: "Extension Number Error" });
}
})
.catch(function (event) {
console.error(event);
});
});
};
render() {
return (
<Row>
<Col span="24" className="lgnpges waitingPage">
<WaitingForCallComponent />
{loader}
</Col>
</Row>
);
}
}
export const mapStateToProps = (state) => {
return {
agentStatus: state?.agentDetails?.agentDetails?.agentStatus,
agentSessionId: state?.agentDetails?.agentDetails?.agentSessionId,
extensionNo: state?.agentDetails?.agentDetails?.extensionNo,
agentId: state?.agentDetails?.agentDetails?.agentId,
genesysId: state?.agentDetails?.agentDetails?.genesysId
};
};
export const mapDispatchToProps = (dispatch) => {
return {
setExtension: (value) => dispatch({ type: SET_AGENT_DETAILS, payLoad: value })
};
};
export default connect(mapStateToProps, mapDispatchToProps)(WaitingContainer);
kindly suggest me the way or either is it required to create class based component for loader
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 :)
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)
I want to display search results on a page. My idea was to make a submit that sends a request to the server. After receiving the data I iterate through it and call a function to process it in another component which should render processed data back to the parent component. Unfortunately no data rendered in the search component and no errors were given to debug.
The main problem right now is that no data is returned from SearchResults; also I'm not sure if the data is received in that component.
import React, { Component } from "react";
import axios from "axios";
import SearchResults from "./SearchResults";
export default class SearchComponent extends Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
query: "",
searchresults: []
};
}
submitSearchResults(searchresults) {
return this.state.searchresults.map(function(object, i) {
return <SearchResults obj={object} key={i} />;
});
}
onSubmit(e) {
e.preventDefault();
axios
.get("http://localhost:4200/serverport/spotify/" + this.state.song)
.then(res => {
const searchresults = res.data;
for (var key in searchresults) {
if (searchresults.hasOwnProperty(key)) {
for (var i = 0; i < searchresults[key].items.length; i++) {
this.submitSearchResults(searchresults[key].items[i]);
}
}
}
});
}
render() {
return (
<div>
<form onSubmit={this.onSubmit}>
<input
type="text"
value={this.state.query}
onChange={this.onChangeSong}
/>
<input value="search" type="submit" />
</form>
<div>{this.submitSearchResults()}</div>
</div>
);
}
}
This is the code that requests the data from server. I removed some irrelevant code to make it more readable.
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
class SearchResults extends Component {
constructor(props) {
super(props);
}
render() {
return <div>works</div>;
}
}
export default SearchResults;
This is the file where the data is supposed to be processed. I only render "works" for debugging.
Instead of calling the function that renders your search result JSX when the request is complete, you can set the searchresults in the state and the component will re-render automatically.
Example
const axios = {
get: () => {
return new Promise(resolve => {
setTimeout(() => {
resolve({ data: [{ text: "foo" }, { text: "bar" }] });
}, 1000);
});
}
};
class SearchComponent extends React.Component {
state = {
song: "",
searchresults: []
};
submitSearchResults(searchresults) {
return this.state.searchresults.map(function(object, i) {
return <SearchResults obj={object} key={i} />;
});
}
onSubmit = e => {
e.preventDefault();
axios
.get("http://localhost:4200/serverport/spotify/" + this.state.song)
.then(res => {
const searchresults = res.data;
this.setState({ searchresults });
});
};
onChangeSong = e => {
this.setState({ song: e.target.value });
};
render() {
return (
<div>
<form onSubmit={this.onSubmit}>
<input
type="text"
value={this.state.song}
onChange={this.onChangeSong}
/>
<input value="search" type="submit" />
</form>
<div>{this.submitSearchResults()}</div>
</div>
);
}
}
class SearchResults extends React.Component {
render() {
return <div>{this.props.obj.text}</div>;
}
}
ReactDOM.render(<SearchComponent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
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!