I am creating a simple login form using React and Redux. My app.js
is:
import React from 'react';
import { render } from 'react-dom';
import Input from 'react-toolbox/lib/input';
import {Button, IconButton} from 'react-toolbox/lib/button';
import PropTypes from 'prop-types';
import * as loginAction from '../actions/loginAction';
class Testing extends React.Component {
onLoginFormSubmit(event) {
event.preventDefault();
this.props.actions.Testing(this.state.username, this.state.password);
}
handleChange(name, value){
let state = this.state;
state[name] = value;
this.setState({state});
console.log(name); // cannot read property of null
console.log(value); // cannot read property of null
}
render() {
console.log(this.props);
return (
<div>
<form name="Login" onSubmit={(e) => this.onLoginFormSubmit(e)}>
<Input type="text" name="username" value="" placeholder="Email Id" tabIndex="1" onChange={this.handleChange.bind(this, 'username')} />
<Input name="password" value="" placeholder="Password" type="password" tabIndex="2" onChange={this.handleChange.bind(this, 'password')} /> <Button type="submit" className="m-t-20 blue-btn" label="Sign in" tabIndex="3" />
</form>
</div>
);
}
}
Testing.propTypes = {
loginAction: PropTypes.object.isRequired,
};
function mapStateToProps(state, ownProps) {
return {
loginResponse: state.loginResponse
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(loginAction, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Testing);
loginAction.js file is:
export function loginError(error){
return { error, type: LOGIN_FAILED };
}
export function loginSuccess(response){
return dispatch => {
dispatch({ response, type: LOGIN_SUCCESS});
};
}
export function loginRequest(username, password){
const user = {username: username, password: password};
return { user, type: LOGIN_ATTEMPT };
}
export function login(username, password) {
console.log("User Data: ", username, password);
return dispatch =>
fetch('url', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: username,
password: password
}),
})
.then(response => {
console.log("I'm here");
if(response.status >= 200 && response.status < 300){
console.log("Response; ", response);
dispatch(loginSuccess(response));
} else {
const error = new Error(response.statusText);
error.response = response;
dispatch(loginError());
throw error;
}
})
.catch(error => { console.log('Request Failed: ', error);});
}
And loginReducer.js file is:
import {
LOGIN_SUCCESS,
LOGIN_FAILED,
LOGIN_ATTEMPT
} from '../actions/loginAction';
import Immutable from 'immutable';
const initialState = new Immutable.Map({
username: '',
password: '',
isLoggingIn: false,
isLoggedIn: false,
error: null
});
export default function user(state = initialState, action){
switch (action.type){
case LOGIN_ATTEMPT:
console.log("LOGIN_ATTEMPT: ",action.user);
return state.merge({
isLoggingIn: true,
isLoggedIn: false,
username: action.user.username,
password: action.user.password
});
case LOGIN_FAILED:
console.log("LOGIN_FAILED: ");
return state.merge({
error: action.error,
isLoggingIn: false,
isLoggedIn: false
});
case LOGIN_SUCCESS:
console.log("LOGIN_SUCCESS: ",action);
return state.merge({
error: null,
isLoggingIn: false,
isLoggedIn: true
})
break;
default:
return state;
}
}
When running the page I am getting this error: Failed prop type: The prop actions
is marked as required in Testing
, but its value is undefined
. Also handleChange
method is throwing following error: Uncaught TypeError: Cannot set property 'username' of null
.
Update: My store.js code is:
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import user from '../reducers/loginReducer';
const store = createStore(
user,
applyMiddleware(thunk)
);
var routes =(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={Main}>
<Route path="/testing" component={Testing}>
</Router>
</Provider>
);
I don't want to use redux-form for now.
The function
handleChange
should get only an event as a parameter.handleChange(e)
this event is attached to the target element, so you can access its values viae.target.value
;With that said, do not
bind
the hendlers in therender
method. do it in theconstructor
as it will create a new instace of thehandler
on eachrender
call. bad for performance. As for theredux
flow, you should useconnect
.export default connect(mapStateToProps, mapDispatchToProps)(Testing)
.EDIT
After another look at your code, beside the fact that you didn't use
connect
to connect the component toredux
, you are mapping a wrong object tomapDispatchToProps
.In this code you are using
loginAction
:But you never imported it, you used a name import:
import { loginSuccess, loginRequest, login } from '../actions/loginAction';
One possible way to import everything and pass it to
mapDispatchToProps
is this:import * as loginAction from '../actions/loginAction';
Another mistake you made is naming this object with different name on
propTypes
, you named itactions
and notloginAction
You will need the same name:
And again don't forget to
connect
!!I believe you need to connect your component to redux.
Don't forget to remove
export default
from before your class as well.Edit: Please use
setState
if you're planning on modifying the local state of a component.