How to update state in react-redux? - javascript

I have made a submit form. I have made userReducer where user[] is array and each array element has firstname, lastname,emailid etc. When I click on submit button it shows array element [0] but when I click on clear button and again try to fill in the form and try to submit no user is added again i.e no state is updated. How to fix this problem ?
Form component (form.js) :
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as action from '../actions/actions';
import './form.css';
class Form extends Component {
constructor(props) {
super(props);
this.setFirstName = this.setFirstName.bind(this);
this.setLastName = this.setLastName.bind(this);
this.setEmailId = this.setEmailId.bind(this);
this.setIban = this.setIban.bind(this);
this.setBankName = this.setBankName.bind(this);
this.showUser = this.showUser.bind(this);
this.reset = this.reset.bind(this);
console.log(this.props);
}
setFirstName(event) {
this.props.dispatch(action.setFirstName(event.target.value));
}
setLastName(event) {
this.props.dispatch(action.setLastName(event.target.value));
}
setEmailId(event) {
this.props.dispatch(action.setEmailId(event.target.value));
}
setIban(event) {
this.props.dispatch(action.setIban(event.target.value));
}
setBankName(event) {
this.props.dispatch(action.setBankName(event.target.value));
}
showUser(){
const jsonobj = this.props;
alert(JSON.stringify(jsonobj));
}
reset(){
this.props.dispatch(action.setFirstName(''));
this.props.dispatch(action.setLastName(''));
this.props.dispatch(action.setEmailId(''));
this.props.dispatch(action.setIban(''));
this.props.dispatch(action.setBankName(''));
}
render(){
return(
<div>
<div id="center">
<form>
<div className="form-group">
<label htmlFor="firstname">First Name:</label>
<input type="firstname" className="form-control" id="firstname" value={this.props.firstname} onChange={this.setFirstName} required/>
</div>
<div className="form-group">
<label htmlFor="lastname">Last Name:</label>
<input type="lastname" className="form-control" id="lastname" value={this.props.lastname} onChange={this.setLastName} required/>
</div>
<div className="form-group">
<label htmlFor="email">Email address:</label>
<input type="email" className="form-control" id="email" value={this.props.emailid} onChange={this.setEmailId} required/>
</div>
<div className="form-group">
<label htmlFor="bankacc">IBAN:</label>
<div id="deletebank" className="items">
<input type="bankacc" className="form-control" id="bankacc" value={this.props.iban} onChange={this.setIban} required/>
<button type="button" className="btn btn-default btn-sm">
<span className="glyphicon glyphicon-trash"></span>
</button>
</div>
</div>
<div className="form-group">
<label htmlFor="bankname">Bank Name:</label>
<input type="bankname" className="form-control" id="bankname" value={this.props.bankname} onChange={this.setBankName} required/>
</div>
<div className="form-group">
<div id="buttons" className="items">
<button type="button" class="btn btn-warning" onClick={this.reset}>Clear Input</button>
<button type="button" className="btn btn-success" onClick={this.showUser}>Submit</button>
</div>
</div>
</form>
</div>
</div>
)}
}
const mapStateToProps = store => {
return {
firstname: store.user.firstname,
lastname: store.user.lastname,
emailid: store.user.emailid,
iban: store.user.iban,
bankname: store.user.bankname
}
}
export default connect(mapStateToProps)(Form);
reducer.js:
const userReducer = (state = {
user:[{
firstname:'',
lastname:'',
emailid:'',
bankaccounts:{
iban:'',
bankname:''
}
}]
}, action) => {
switch (action.type) {
case 'SET_FIRSTNAME':{
return {
...state,
user:{...state.user, firstname: action.payload}
}
}
case 'SET_LASTNAME':{
return {
...state,
user:{...state.user, lastname: action.payload}
}
}
case 'SET_EMAILID':{
return {
...state,
user:{...state.user, emailid: action.payload}
}
}
case 'SET_IBAN':{
return {
...state,
user:{...state.user, iban: action.payload}
}
}
case 'SET_BANKNAME':{
return {
...state,
user:{...state.user, bankname: action.payload}
}
}
default: return state;
}
}
export default userReducer;
Actions.js:
export const SET_FIRSTNAME = 'SET_FIRSTNAME';
export const SET_LASTNAME = 'SET_LASTNAME';
export const SET_EMAILID = 'SET_EMAILID';
export const SET_IBAN = 'SET_IBAN';
export const SET_BANKNAME = 'SET_BANKNAME';
export function setFirstName(firstname){
return {
type:SET_FIRSTNAME,
payload:firstname
}
}
export function setLastName(lastname){
return {
type:SET_LASTNAME,
payload:lastname
}
}
export function setEmailId(emailid){
return {
type:SET_EMAILID,
payload:emailid
}
}
export function setIban(iban){
return {
type:SET_IBAN,
payload:iban
}
}
export function setBankName(bankname){
return {
type:SET_BANKNAME,
payload:bankname
}
}
store.js:
import { createStore } from 'redux';
import userReducer from './reducers/reducers';
const store = createStore(userReducer);
store.subscribe(() => {
console.log('Store changed', store.getState());
})
export default store;
Screenshot:

There's a bunch of stuff to address here, but the primary issue is that your state is expecting user to be an array. I think it would be very wise to rename this users as to not get confused:
(I'm going to remove some keys to make this easier)
const initialState = {
users: [ // note "users" not "user"
{
firstname: '',
lastname: '',
},
],
};
Your reducer switch statements don't specify WHICH user it should be updating. If you want to just start with "adding" a new user it might look something like this:
const userReducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_USER": {
return {
...state,
users: [...state.users, action.payload],
};
}
default:
return state;
}
};
Here we add a new user to the users array in the form of action.payload which should contain all the keys you want on your user.
You can now have an action creator that's a bit more concise
const addUser = user => ({
type: 'ADD_USER',
payload: user,
})
And your form could be simplified a lot:
import * as actions from './actions'
class Form extends React.Component {
state = {
firstname: '',
lastname: '',
}
render() {
return (
<form onSubmit={() => {
this.props.dispatch(actions.addUser(this.state))
}}>
<input
value={this.state.firstname}
onChange={e => this.setState({ firstname: e.target.value })
/>
<input
value={this.state.lastname}
onChange={e => this.setState({ lastname: e.target.value })
/>
</form>
)
}
}
export default connect()(Form)

Related

How to dispatch async action in Redux using Redux Thunk: TypeError: Cannot read property 'loading' of undefined

I have set everything in Redux side and I see every action in Redux Devtool. It works all perfect. The problem occurs when I want to dispatch action in React Component. In login component I want to dispatch action, wait its response then depending on response redirect it to a page or show errors. Here are my codes:
userActions.js
import axios from "axios";
import {
LOGIN_USER,
LOGIN_USER_SUCCESS,
LOGIN_USER_FAILED,
} from "./types";
const loginUserRequest = () => {
return {
type: LOGIN_USER,
};
};
const loginUserSuccess = (user) => {
return {
type: LOGIN_USER_SUCCESS,
payload: user,
};
};
const loginUserFailed = (error) => {
return {
type: LOGIN_USER_FAILED,
payload: error,
};
};
export const loginUser = (dataSubmitted) => {
return (dispatch) => {
dispatch(loginUserRequest());
axios
.post("/api/users/login", dataSubmitted)
.then((response) => {
dispatch(loginUserSuccess(response.data));
})
.catch((err) => {
dispatch(loginUserFailed(err));
});
};
};
userReducer.js:
import {
LOGIN_USER,
LOGIN_USER_SUCCESS,
LOGIN_USER_FAILED,
} from "../actions/types";
const initialState = {
loading: false,
user: "",
error: "",
};
export default function (state = initialState, action) {
switch (action.type) {
case LOGIN_USER:
return { ...state, loading: true };
case LOGIN_USER_SUCCESS:
return { ...state, loading: false, user: action.payload, error: "" };
case LOGIN_USER_FAILED:
return { ...state, loading: false, user: "", error: action.payload };
default:
return state;
}
}
The above codes works great and does the job. The problem is in following code where I am dispatching the async action. After I run the code I get this.props.userData as undefined.
Login.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { loginUser } from "../../actions/userActions";
export class Login extends Component {
state = {
email: "",
password: "",
errors: [],
};
displayErrors = (errors) =>
errors.map((error, i) => (
<div className="alert alert-danger" key={i}>
{error}
</div>
));
handleSubmit = (e) => {
e.preventDefault();
var { email, password } = this.state;
var errors = [];
if (email === "") {
errors.push("Email is required");
}
if (password === "") {
errors.push("Password is required");
}
this.setState({ errors: errors });
//Problem occurs here
this.props.dispatch(loginUser({ email, password }));
if (response.payload.success) {
sessionStorage.setItem("jwt", response.payload.token);
sessionStorage.setItem("userId", response.payload._id);
this.props.history.push("/");
} else {
errors.push("Username and/or Password is not correct");
this.setState({ errors: errors });
}
};
render() {
return (
<form className="form-signin" onSubmit={this.handleSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Sign in</h1>
{this.state.errors.length > 0 && this.displayErrors(this.state.errors)}
<label for="inputEmail" className="sr-only">
Email address
</label>
<input
type="email"
id="inputEmail"
className="form-control"
placeholder="Email address"
value={this.state.email}
onChange={(e) => {
this.setState({ email: e.target.value });
}}
required
autoFocus
/>
<label for="inputPassword" className="sr-only">
Password
</label>
<input
type="password"
id="inputPassword"
className="form-control"
placeholder="Password"
value={this.state.password}
onChange={(e) => {
this.setState({ password: e.target.value });
}}
required
/>
<div className="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me" /> Remember me
</label>
</div>
<button
className="btn btn-lg btn-primary btn-block"
type="submit"
onClick={this.handleSubmit}
>
Sign in
</button>
<Link to="/register">Sign Up</Link>
</form>
);
}
}
function mapStateToProps(state) {
return {
userData: state.user,
};
}
export default connect(mapStateToProps)(Login);
You need to install connected-react-router to manipulate with a history inside redux:
import { push } from 'connected-react-router';
export const loginUser = (dataSubmitted) => {
return (dispatch) => {
dispatch(loginUserRequest());
axios
.post("/api/users/login", dataSubmitted)
.then((response) => {
dispatch(loginUserSuccess(response.data));
if (response.payload.success) {
sessionStorage.setItem("jwt", response.payload.token);
sessionStorage.setItem("userId", response.payload._id);
push("/");
} else {
errors.push("Username and/or Password is not correct");
}
})
.catch((err) => {
dispatch(loginUserFailed(err));
});
};
};

Update component when the store changes

I'm trying to update a component based on store updates, the objective of that is, when I click on a table item, I want to update the form buttons to edit form, and Edit the table item.
My source code:
I have an action which updates currentUser. currentUser is the user I want to update
src/actions/user.js
export const updateCurrentUserSuccess = (currentUser) => {
return {
type: UPDATE_CURRENT_USER,
currentUser
}
}
export const updateCurrentUser = (id) => {
return (dispatch) => {
return axios.get(`${apiUrl}/users/${id}`)
.then(response => {
console.log(response.data.data)
dispatch(updateCurrentUserSuccess(response.data.data))
})
.catch(error => {
throw (error);
});
};
};
my currentUserReducer:
src/reducers/currentUserReducer.js
import { UPDATE_CURRENT_USER } from '../constants/ActionTypes';
const initialState = {
currentUser: [],
}
export default function userReducer(state = initialState, action) {
switch (action.type) {
case UPDATE_CURRENT_USER:
return action.currentUser;
default:
return state;
}
}
now my components:
my NewUser form:
src/components/NewUser.js
import React, { Component } from 'react';
import { Store } from '../store'
class NewUser extends Component {
state = {
id: '',
name: '',
cpfcnpj: '',
isEdit: false
};
componentDidMount(){
this.handleUserChange()
}
handleInputChange = e => {
this.handleUserChange();
this.setState({
[e.target.name]: e.target.value
});
};
handleSubmit = e => {
e.preventDefault();
if (!this.state.isEdit) {
if (this.state.name.trim() && this.state.cpfcnpj.trim()) {
this.props.onAddUser(this.state);
this.handleReset();
}
} else {
if (this.state.name.trim() && this.state.cpfcnpj.trim() && this.state.id !== '') {
this.props.onEdit(this.state);
this.handleReset();
}
}
};
handleReset = () => {
Store.getState().currentUser = []
this.setState({
id: '',
name: '',
cpfcnpj: '',
isEdit: false
});
};
handleUserChange() {
console.log('store', Store.getState().currentUser._id);
if (Store.getState().currentUser._id !== undefined) {
this.setState({
id: Store.getState().currentUser._id,
name: Store.getState().currentUser.name,
cpfcnpj: Store.getState().currentUser.cpfcnpj,
isEdit: true
});
}
}
render() {
return (
<div>
<form className="form-inline" onSubmit={this.handleSubmit}>
<div className="form-group margin-right">
<input
type="text"
placeholder="Name"
className="form-control"
name="name"
onChange={this.handleInputChange}
value={this.state.name}
/>
</div>
<div className="form-group margin-right">
<input
type="text"
placeholder="CPF/CNPJ"
className="form-control"
name="cpfcnpj"
onChange={this.handleInputChange}
value={this.state.cpfcnpj}>
</input>
</div>
<div className="form-group">
<button type="submit" className={this.state.isEdit ? "btn btn-success margin-right hidden" : "btn btn-success margin-right"}>
<span className="glyphicon glyphicon-plus" aria-hidden="true"></span>
Adicionar
</button>
<button type="submit" className={this.state.isEdit ? "btn btn-primary margin-right" : "btn btn-primary margin-right hidden"}>
<span className="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>
Salvar
</button>
<button type="button" className="btn btn-default margin-right" onClick={this.handleReset}>
<span className="glyphicon glyphicon-erase" aria-hidden="true"></span>
Limpar
</button>
</div>
</form>
</div>
);
}
}
export default NewUser;
my component User item:
***src/components/User.js***
import React from 'react';
export default ({ user: { name, cpfcnpj, _id }, onDelete, onEditUser }) => {
return (
<tr>
<th scope="row">{name}</th>
<td>{cpfcnpj}</td>
<td>
<button className="btn btn-warning btn-xs margin-right" type="button" onClick={() => onEditUser(_id)}>
<span className="glyphicon glyphicon-edit" aria-hidden="true"> </span>
Editar
</button>
<button className="btn btn-danger btn-xs margin-right" type="button" onClick={() => onDelete(_id)}>
<span className="glyphicon glyphicon-trash" aria-hidden="true"> </span>
Excluir
</button>
</td>
</tr>
);
};
now my smart components:
src/containers/UserList.js
import React from 'react';
import { connect } from 'react-redux';
import User from '../components/User';
import { deleteUser, updateCurrentUser } from '../actions/user';
import NewUser from '../components/NewUser';
function UserList({ users, onDelete, onEditUser }) {
if (!users.length) {
return (
<div className="margin-top">
No Users
</div>
)
}
return (
<div className="margin-top">
<table className="table table-striped">
<thead>
<tr>
<th scope="col">Nome</th>
<th scope="col">CPF/CNPJ</th>
</tr>
</thead>
<tbody>
{users.map(user => {
return (
<User user={user} onDelete={onDelete} onEditUser={onEditUser} key={user._id} />
);
})}
</tbody>
</table>
</div>
);
}
const mapStateToProps = state => {
return {
users: state.users
};
};
const mapDispatchToProps = dispatch => {
return {
onDelete: id => {
dispatch(deleteUser(id));
},
onEditUser: (id) => {
dispatch(updateCurrentUser(id))
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserList, NewUser);
src/containers/CreateUser.js
import { connect } from 'react-redux';
import { createUser, updateUser } from '../actions/user';
import NewUser from '../components/NewUser';
const mapDispatchToProps = dispatch => {
return {
onAddUser: user => {
dispatch(createUser(user));
},
onEdit: (id, name, cpfcnpj) => {
dispatch(updateUser(id, name, cpfcnpj))
}
};
};
export default connect(
null,
mapDispatchToProps
)(NewUser);
src/App.js
import React, { Component } from 'react';
import CreateUser from './containers/CreateUser';
import UserList from './containers/UserList';
import './css/main.css'
class App extends Component {
render() {
return (
<div className="container">
<h1 className="styles-app">Usuários</h1>
<div className="row styles-app">
<div className="col-md-12">
<CreateUser />
</div>
<div className="col-md-12">
<UserList />
</div>
</div>
</div>
);
}
}
export default App;
Here is something you might try. Connect your NewUser.js to the store.
import { connect } from 'react-redux;
export default connect(mapStateToProps)(NewUser);
Then map your currentUser state to props.
const mapStateToProps = state => {
return {
currentUser: state.currentUser
};
};
In your currentUserReducer
initialState = {
//Assuming these are the only values in response
id: '',
name: '',
cpfcnpj: '',
isEdit: false
};
export default function userReducer(state = initialState, action) {
switch (action.type) {
case UPDATE_CURRENT_USER:
return {
...state,
id: action.currentUser.id,
name: action.currentUser.name,
cpfcnpj: action.currentUser.cpfcnpj,
isEdit: true
};
default:
return state;
}
}
You should have access to the current user object now in props.
Then in your input value field
value={this.props.currentUser.name}
value={this.props.currentUser.cpfcnpj}
You may also need to do a check to see if these values have been updated. Also, not sure if the placeholder text might interfere.
Hope this gets you closer to the solution.
Edit
In the case of clearing props, you might just add another action to do so.
In your actions for currentUser:
export const clearUserData = () => {
return {
type: CLEAR_USER_DATA,
}
}
And in your reducer:
export default function userReducer(state = initialState, action) {
switch (action.type) {
case UPDATE_CURRENT_USER:
return {
...state,
id: action.currentUser.id,
name: action.currentUser.name,
cpfcnpj: action.currentUser.cpfcnpj,
isEdit: true
};
case CLEAR_USER_DATA:
return {
...state,
id: '',
name: '',
cpfcnpj: '',
isEdit: false
};
default:
return state;
}
}
Add the clearUserData action to execute after you submit your edits and it should reset your reducer. You might even be able to just do
return {
...state,
initialState
};

How could I save user entry inside a state in Redux?

I need to fire the action to update storyTextValue inside StoryTextReducer so I can use it as this.props.storyTextValue in SearchArticle.
In other words, in CreateArticle upon the user typing in <textarea> followed by clicking Submit, I want whatever they've typed in to be stored in storyTextValue which would allow me to use this.props.storyTextValue in SearchArticle to display the text.
What am I doing wrong and how can I achieve this?
Here's CreateArticle:
import React, { Component } from 'react';
import {connect} from "react-redux";
import * as actionType from "../../store/actions/actions";
class CreateArticle extends Component {
constructor(props) {
super(props);
}
handleSubmit = event => {
event.preventDefault();
this.setState({storyTextValue: event.target.storyTextValue});
this.props.storyTextValueRedux(event.target.storyTextValue);
}
handleStoryText = event => {
event.preventDefault();
this.setState({value: event.target.value});
}
onSubmit = () => {
if(this.props.storyTextValue === "") {
alert("Please enter the value and then click submit");
} else {
alert("Article saved " + '\n' + this.props.storyTextValue);
}
}
render() {
return(
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.props.cityCodeValue} type="text" placeholder="city code"/>
<input type="text" placeholder="author name"/>
<textarea value={this.props.storyTextValue} onChange={this.handleStoryText} rows="2" cols="25" />
<button type="submit" value="Submit" onClick={() => this.onSubmit()}>Submit</button>
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
articleIdValue: state.articleIdValue.articleIdValue,
storyTextValue: state.storyTextValue.storyTextValue
};
};
const mapDispatchToProps = dispatch => {
return {
articleIdValueRedux: (value) => dispatch({type: actionType.ARTICLE_ID_VALUE, value}),
storyTextValueRedux: (value) => dispatch({type: actionType.STORY_VALUE, value})
};
};
export default connect(mapStateToProps, mapDispatchToProps)(CreateArticle);
Here's SearchArticle:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actionType from '../../store/actions/actions';
class SearchArticle extends Component {
constructor(props) {
super(props);
this.state = {
flag: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
this.props.CityCodeReducerRedux(event.target.value);
}
handleSubmit(event) {
this.setState({flag: true});
event.preventDefault();
}
displayName = () => {
if(this.props.cityCodeValue === "nyc" || this.props.articleIdValue === 1) {
return(
<div>
<p>author name: {this.props.authorNameValue}</p>
<p>article text: {this.props.storyTextValue}</p> {/* want to display story text here */}
</div>
);
}
}
render() {
return(
<div>
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.props.cityCodeValue} type="text" placeholder="city code"/>
<input onChange={this.handleChange} value={this.props.articleIdValue} placeholder="article id"/>
<button onClick={() => this.displayName} value="Search">Submit</button>
{this.state.flag ? this.displayName() : null}
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
cityCodeValue: state.cityCodeValue.cityCodeValue,
authorNameValue: state.authorNameValue.authorNameValue,
articleIdValue: state.articleIdValue.articleIdValue,
storyTextValue: state.storyTextValue.storyTextValue
};
};
const mapDispatchToProps = dispatch => {
return {
CityCodeReducerRedux: (value) => dispatch({type: actionType.CITY_CODE_VALUE, value}),
articleIdValueRedux: (value) => dispatch({type: actionType.ARTICLE_ID_VALUE, value})
};
};
export default connect(mapStateToProps, mapDispatchToProps)(SearchArticle);
Here's StoryTextReducer:
import * as actionType from '../store/actions/actions';
const initialState = {
storyTextValue: ''
};
const StoryTextReducer = (state = initialState, action) => {
switch (action.type) {
case actionType.STORY_VALUE:
return {
...state,
storyTextValue: action.value
};
default:
return state;
}
};
export default StoryTextReducer;
In handleSubmit of CreateArticle please console.log(event.target.storyTextValue).... i would think it is undefined. I think you would prefer this.props.storyTextValueRedux(event.target.value);

React/Redux: redirect after loggin

Am new to react/redux.I have a Redux action for authentication, and after that I need to redirect to a confirmation page home. I don't know how to redirect
this is my index.js
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import {loginAction} from './actions';
export class Login extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
username: '',
password: ''
};
// this.handleChange = this.handleChangeUsername.bind(this);
// this.handleSubmit = this.handleChangePassword.bind(this);
}
handleChangeUsername(event) {
console.log(event.target.value);
this.setState({username: event.target.value});
console.log(this.state.username)
}
handleChangePassword(event) {
this.setState({password: event.target.value});
console.log(this.state.password)
}
handleSubmit(event) {
this.props.dispatch(loginAction(this.state));
event.preventDefault();
}
render() {
return (
<div>
<div className="loginColumns animated fadeInDown">
<div className="row">
<div className="col-md-6">
</div>
<div className="col-md-6">
<div className="ibox-content">
<form className="m-t" role="form" onSubmit={this.handleSubmit.bind(this)}>
<div className="form-group">
<input type="email" value={this.state.username} onChange={this.handleChangeUsername.bind(this)} className="form-control" placeholder="Username" required="" />
</div>
<div className="form-group">
<input type="password" value={this.state.password} onChange={this.handleChangePassword.bind(this)} className="form-control" placeholder="Password" required="" />
</div>
<button type="submit" className="btn btn-primary block full-width m-b">Login</button>
<a href="#">
<small>Forgot password?</small>
</a>
</form>
</div>
</div>
</div>
<hr/>
<div className="row">
<div className="col-md-6">
Copyright Example Company
</div>
<div className="col-md-6 text-right">
<small>© 2014-2015</small>
</div>
</div>
</div>
</div>
);
}
}
Login.propTypes = {
dispatch: PropTypes.func.isRequired,
};
const mapStateToProps = createStructuredSelector({
// Login: makeSelectLogin(),
});
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
action.js
import {
DEFAULT_ACTION,
LOGIN_ACTION
} from './constants';
export function defaultAction() {
return {
type: DEFAULT_ACTION,
};
}
export function loginAction(json) {
return {
type: LOGIN_ACTION,
json
};
}
reducer.js
import { fromJS } from 'immutable';
import {
DEFAULT_ACTION,
LOGIN_ACTION
} from './constants';
const initialState = fromJS({
status: false
});
function loginReducer(state = initialState, action) {
console.log("action", action)
switch (action.type) {
case DEFAULT_ACTION:
return state;
case LOGIN_ACTION:
return _testFunction(state,action.json);
default:
return state;
}
}
function _testFunction(state, json) {
if(json.username == "abcd#gmail.com" && json.password == "1234")
return state.set("status", true)
}
export default loginReducer;
i want to redirect /home after successful login. how can i redirect?
Well, what you have to do is something like this. Pass a callback to your action. Once action completed it work, merely invoke that callback, which will programmatically redirect you to the url you need. Upon the submission of the login form pass the input values with the callback function to your action like this.
onSubmit(values) {
this.props.loginAction(values, () => {
this.props.history.push('/');
});
}
Then in your action, if you are calling a backend API, when the promise is resolved make sure you invoke the callback function that gets passed in to the action like this.
export function loginAction(values, callback) {
const request = axios.post(`/endpoint`, values)
.then(() => callback());
return {
type: LOGIN,
payload: request
};
}
This is all what you have to do. It is up to you to make slight alterations to this depending on your scenario and setup. I'll keep it for you as an exercise. Hope this helps. Happy Coding !

Store validation in React and Redux

I have simple page, which identifies user, it consists of Name and Email.
class UserIdentification extends Component {
constructor(props) {
super(props);
this.state = {
Name: this.props.Name,
Email: this.props.Email,
emailErrors: ''
};
}
isValid(showErrors, validate, errorsName, value) {
let errors = [];
for (let name of validate) {
errors = errors.concat(validators[name](value));
}
if (showErrors) {
this.setState({
[errorsName]: errors.join(' ')
});
}
console.log(errors);
return !errors.length;
}
handleInputChange(name, event) {
this.setState({
[name]: event.target.value,
});
}
onInputBlur(showErrors, validate, errorsName, value, action) {
if (!this.isValid(showErrors, validate, errorsName, value))
return;
this.props.userIdentificationActions[action](value);
}
render() {
return (
<div className='row'>
<form className='col s12'>
<h5 className='flow-text'>Identification</h5>
<div className='row'>
<div className='input-field col s12'>
<i className='material-icons prefix'>account_circle</i>
<input
value={this.props.Name}
placeholder='Enter your name'
id='person_name'
onChange={this.handleInputChange.bind(this, 'Name')}
onBlur={this.onInputBlur.bind(this, false, [], '', this.state.Name, 'setName')}
type='text' />
<label className='active' htmlFor='person_name'>Name</label>
</div>
</div>
<div className='row'>
<div className='input-field col s12'>
<i className='material-icons prefix'>email</i>
<input
value={this.props.Email}
placeholder='e.g. myemail#example.com'
id='email'
type='email'
onChange={this.handleInputChange.bind(this, 'Email')}
onBlur={this.onInputBlur.bind(this, true, ['email'], 'emailErrors', this.state.Email, 'setEmail')}
className={this.state.emailErrors.length ? 'invalid' : 'valid'}
/>
<label className='active' data-error={this.state.emailErrors} htmlFor='email'>Email</label>
</div>
</div>
</form>
</div>
);
}
}
function mapStateToProps(state) {
return {
Name: state.userIentification.Name,
Email: state.userIdentification.Email,
}
}
function mapDispatchToProps(dispatch) {
return {
userIdentificationActions: bindActionCreators(userIdentificationActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(userIdentification)
Validation of single fields works perfect.
Here my actions:
export function setName(name) {
return {
type: actionTypes.SET_NAME,
payload: name
}
}
export function setEmail(email) {
return {
type: actionTypes.SET_EMAIL,
payload: email
}
}
and reducer:
const initialState = {
Name: '',
Email: ''
}
export default function identification(state = initialState, action) {
switch (action.type) {
case actionTypes.SET_NAME:
return { ...state, Name: action.payload };
case actionTypes.SET_EMAIL:
return { ...state, Email: action.payload };
default:
return state;
}
}
What I want, is to make validation of store, for example, when Name is not empty and Email is valid, then dispatch some needed action.
What should I do and where to implement this functionality?

Categories