0

I am trying to make a transition to redux in developing my web app.

I realized that after I specify the types in my actions, I can dispatch it to my reducers and the reducers will perform changes based on the types of the actions.

I have two actions that looks like this:

export const doLogin = (username, password) => {
    return {
        type: types.AUTH_LOGIN,
        username,
        password
    };
};

export const doLogout = () => ({
  type: types.AUTH_LOGOUT,
});

I can dispatch these two actions to my reducer that looks like:

const initialState = {
  username: "",
  isLoggedIn: false
};

const authReducer = (state = initialState, action) => {
  switch (action.type) {
    case types.AUTH_LOGIN:
      return {
        ...state, // Preserve states that are unchanged
        username: action.username, // Values to be changed
        isLoggedIn: true
      };
    case types.AUTH_LOGOUT:
      return initialState;
    default:
      return state;
  }
};

My reducer currently just creates a new state username: username, isLoggedin: true and returns it. It has NOTHING to do with actual user authentication.

Before switching to Redux, I did my user authentication by calling an API Post using Axios.

The code looked like:

 axios.post('http://localhost:5000/signin', 
 {
     username:this.state.username, 
     password:this.state.password
 })
     .then(function (response) {

     console.log(response);
     if(response.data.status === "Successful"){
         console.log("Login successfull");
         this.setState({loggedOn:true})
     }

     else{
         console.log("There is no user with the given username and password");
         //alert("Username does not exist");
     }
     }
 )
 .catch(function (error) {

 console.log(error);
 });

I am not sure how and where this has to be incorporated to Redux. Do I have to put it into my reducers, or actions?

Please help.

EDIT

I am already using redux-thunk

store.js

import rootReducer from "../reducers";
import ReduxThunk from "redux-thunk";

const middlewares = [ReduxThunk];
const store = createStore(rootReducer, applyMiddleware(...middlewares));

index.js

import store from "./store";
import { createStore } from 'redux';
import reducers from './reducers';
import { Provider } from 'react-redux';


console.log('init state', store.getState());
store.subscribe(()=>console.log(store.getState()));
store.dispatch(actions.doLogin('sample', '1234'));

actions.doLogin gives me the error Actions must be plain objects. Use custom middleware for async actions.

2 Answers 2

3

I use the dispatch function as below

export const doLogin = (username, password) => {
    return dispatch => {
        axios.post('http://localhost:5000/signin', {
            username: username,
            password: password
        })
        .then((response)=>{
            dispatch({
                type: AUTH_LOGIN,
                username: response.username
            })
        })
        .catch((error)=>{
            console.log("There is no user with the given username and password");
            //alert("Username does not exist");
        })
    };
};
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! It seems like it's working. Could you please explain how it actually works step-by-step? What does the syntax dispatch => { ... do? And how does the second dispatch in the code work?
That's a function used from redux-thunk that waits for the request to response, and then return what you return from the second dispatch
In my understanding, doing dispatch(functionName) will send the action to the reducer. In reducer, it will return new states based on the action type. Is it correct? In your answer, I am confused because I am not sure what happens if we pass in an object to a dispatch, not a function.
1

The reducers just receive the state change. So, your action should contain the authentication. Then, when it's complete and the user was authenticated, it returns to the reducer. Like this:

export const doLogin = (username, password) => {
     var auth = false
     axios.post('http://localhost:5000/signin', 
     {
         username:this.state.username, 
         password:this.state.password
     }).then(response => {
         console.log(response);
         if(response.data.status === "Successful"){
             console.log("Login successfull");
             auth = true;
         }
         else{
             console.log("There is no user with the given username and password");
             //alert("Username does not exist");
         }
      }).catch(function (error) {
           console.log(error);
      });
      if(auth) return {
             type: types.AUTH_LOGIN,
             username,
             password
      }
};

export const doLogout = () => ({
  type: types.AUTH_LOGOUT,
});

Advise: That's not a good pratice. It's better to maintain the authentication in a script or inside the component. Then, when it's sucessful, it emmits the action to the Redux. I'd tried this a couple times, and noticed that it's not the best way.

5 Comments

Looks great. But when I try to test it by doing store.dispatch(actions.doLogin('sample', '1234'));, it gives me an error saying Actions must be plain objects. Use custom middleware for async actions. Any idea?
That's the problem. The axios.post() is async, and your action cannot be async. Take a look at Redux-Thunk, it makes it possible.
The thing is, I am already using Redux-Thunk. Please take a look at my edit.
Read this article medium.com/@stowball/…
Thanks that is a useful link and I will look into it. However, are you able to point out the reason why the error comes up even I use redux-thunk?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.