Redux action is working but reducer is not
Redux action is working but reducer is not
Not sure why my reducer is not working. I have a console.log in my actions file. So for example, when I test out a failed login attempt, I see the console log in both the loginFromAPI function and loginFailure function (see in actions.js below). These are outputting what is expected. But for some reason, the login reducer is not working. When I console.log in the loginReducer.js, I see nothing. This tells me that although the action is working, the reducer is not. My redux props do not change from the initial state (I've checked this by outputting to the console). I'd appreciate any help solving this issue.
Here is the file structure:
app
reducers
loginReducer.js
rootReducer.js
screens
Login
Login.js
index.js
actions.js
store
configureStore.js
App.js
actionTypes.js
File configureStore.js
configureStore.js
import { createStore, applyMiddleware } from 'redux';
import app from '../reducers/rootReducer';
import thunk from 'redux-thunk';
import {createLogger} from 'redux-logger';
export default function configureStore() {
const logger = createLogger();
let store = createStore(app, applyMiddleware(thunk, logger));
return store;
}
File rootReducer.js
rootReducer.js
import { combineReducers } from 'redux';
import loginReducer, * as fromLogin from './loginReducer';
export default combineReducers({
login: loginReducer
})
export const getLogin = (state) => fromLogin.getLogin(state.login)
File loginReducer.js
loginReducer.js
import { LOGIN, LOGIN_SUCCESS, LOGIN_FAILURE } from '../actionTypes';
const initialState = {
user: {},
isFetching: false,
isLoggedIn: false,
error: false,
errorMessage: "",
}
export default function loginReducer(state = initialState, action) {
console.log(action); <-- I do not see this
switch(action.type) {
case LOGIN:
return {
...state,
isFetching: true,
user: {}
}
case LOGIN_SUCCESS:
return {
...state,
isFetching: false,
isLoggedIn: true,
user: action.user
}
case LOGIN_FAILURE:
return {
isFetching: false,
error: true,
errorMessage: action.errorMessage
}
default:
return state
}
}
export const getLogin = (state) => ({
user: state.user,
isFetching: state.isFetching,
isLoggedIn: state.isLoggedIn,
error: state.error,
errorMessage: state.errorMessage
})
File actions.js
actions.js
import { LOGIN, LOGIN_SUCCESS, LOGIN_FAILURE } from '../../actionTypes';
import axios from 'axios';
export function loginFromAPI(loginInfo) {
login();
axios({
method: 'POST',
url: 'SOME_URL_GOES_HERE',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
data: JSON.stringify({
email: loginInfo.email,
password: loginInfo.password
}),
})
.then((response) => {
loginSuccess(response.data);
console.log(response.data);
})
.catch(error => {
switch(error.response.status) {
case 404:
console.log("No user with that email.");
loginFailure("No user with that email.")
break;
case 401:
console.log("Invalid password.");
loginFailure("Invalid password.")
break;
default:
console.log("There was an error loggin in");
loginFailure("There was an error loggin in");
break;
}
})
}
function login() {
return {
type: LOGIN,
}
}
function loginSuccess(user) {
return {
type: LOGIN_SUCCESS,
user
}
}
function loginFailure(errorMessage) {
console.log(errorMessage); <-- I see this output
return {
type: LOGIN_FAILURE,
errorMessage
}
}
File index.js
index.js
import Login from './Login';
import {connect} from 'react-redux';
import * as actions from './actions';
import {getLogin} from '../../reducers/rootReducer';
const mapStateToProps = (state) => ({
...getLogin(state),
})
const mapDispatchToProps = () => ({
...actions
})
export default connect(mapStateToProps, mapDispatchToProps)(Login)
File Login.js
Login.js
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: null,
password: null,
}
this.login = this.login.bind(this);
}
login() {
this.props.loginFromAPI({email: this.state.email, password: this.state.password});
console.log(this.props) <-- This never changes but does show up
}
render() {
let error = this.props.error
return (
<ImageBackground source={require('../../assets/Signup.png')} style={styles.container}>
<View style={styles.content}>
...
Some text input Stuff
...
{error &&
<Text>{this.props.errorMessage}</Text>
}
<Button
onPress={this.login}
/>
...
);
}
}
2 Answers
2
So for an action to work, you need to return an object containing the key "type" that matches your reducer switch case.
{
type: "MY_ACTION_TYPE"
}
However your function loginFromAPI is async, and thus you can't just return an object from it. To get around this you can use redux middlewares.
Two of the most popular are redux-thunk, and redux-saga. redux-thunk is much simpler.
Thunk example:
export function loginFromAPI(loginInfo) {
return function (dispatch) {
dispatch(login())
login()
.then(res => dispatch(loginSuccess()) // the action you want to dispatch
}
}
https://www.npmjs.com/package/redux-thunk
https://github.com/redux-saga/redux-saga
You do not dispatch your action in mapDispatchToProps.
https://redux.js.org/basics/actions
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Comments
Post a Comment