2

I use JWT token authentication for auth.

When I access to localhost:4000/api/refresh with token, it verify if token is expired, and return refreshed token with status code 200.

And middleware detect if token is valid and return to 200 or 401.

Backend is works perfectly. But frontend got some errors.

I use redux for global state manage.

Here is my codes.

[reducers.js]

// Actions
const LOGIN_TRUE = 'LOGIN_TRUE';
const LOGIN_FALSE = 'LOGIN_FALSE';
const CHECK_TOKEN = 'CHECK_TOKEN';
const REFRESH_TOKEN = 'REFRESH_TOKEN';

// Action Creators
function loginTrue() {
    return {
        type: LOGIN_TRUE
    }
}
function loginFalse() {
    return {
        type: LOGIN_FALSE
    }
}
function checkToken() {
    return {
        type: CHECK_TOKEN
    }
}
function refreshToken() {
    return {
        type: REFRESH_TOKEN
    }
}

// Reducer
const initialState = {
    isLogin: false
}

function reducer(state = initialState, action) {
    switch(action.type) {
        case LOGIN_TRUE:
            return applyLoginTrue(state, action);
        case LOGIN_FALSE:
            return applyLoginFalse(state, action);
        case CHECK_TOKEN:
            return applyCheckToken(state, action);
        case REFRESH_TOKEN:
            return applyRefreshToken(state, action);
        default:
            return state;
    }
}

// Reducer Functions
function applyLoginTrue(state) {
    return {
        ...state,
        isLogin: true
    }
}

function applyLoginFalse(state) {
    return {
        ...state,
        isLogin: false
    }
}

function applyCheckToken(state) {
    const token = localStorage.getItem('token');
    if(token !== null) {
        return {
            ...state,
            isLogin: true
        }
    } else {
        return {
            ...state,
            isLogin: false
        }
    }
}

function applyRefreshToken(state) {
    console.log(state);
    const token = localStorage.getItem('token');
    if(token !== null) {
        fetch("http://localhost:4000/api/refresh", {
            method: "POST",
            headers: {
                'Authorization':`JWT ${token}`
            }
        })
        .then(res => {
            if(res.status === 200) {
                return res.json();
            } else {
                console.log("applyRefreshToken() res.status is not 200");
            }
        })
        .then(json => {
            localStorage.clear();
            localStorage.setItem('token', json.token);
            return {
                ...state,
                isLogin: true
            }
        })
    } else {
        console.log("applyRefreshToken() token is null");
        return {
            ...state,
            isLogin: false
        }
    }
}

// Export Action Creators
export const actionCreators = {
    loginTrue,
    loginFalse,
    checkToken,
    refreshToken
};

// Export Reducer
export default reducer;

After wrote the reducer.js, I made dummy component to test it.

componentDidMount() {
    const { refreshToken } = this.props;
    refreshToken();
}
render () {
    const { isLogin } = this.props;
    return (
        <div className="wrap">
            { isLogin ? "Under Construction" : "Login please" }
        </div>
    )
}
export default connect(mapStateToProps, mapDispatchToProps)(index);

But it throw errors like this -> TypeError: Cannot read property 'isLogin' of undefined

I can't find where is the error occured.

Because loginTrue(), loginFalse(), checkToken() works perfectly.

Is there any solution about this?

Thanks.

[mapStateToProps.js]

const mapStateToProps = (state) => {
    const { isLogin } = state;
    return {
        isLogin
    }
}

export default mapStateToProps;

[mapDispatchToProps.js]

import { bindActionCreators } from 'redux';
import { actionCreators } from './reducer';

const mapDispatchToProps = (dispatch) => {
    return {
        loginTrue: bindActionCreators(actionCreators.loginTrue, dispatch),
        loginFalse: bindActionCreators(actionCreators.loginFalse, dispatch),
        checkToken: bindActionCreators(actionCreators.checkToken, dispatch),
        refreshToken: bindActionCreators(actionCreators.refreshToken, dispatch)
    }
}

export default mapDispatchToProps;
Hide
  • 3,199
  • 7
  • 41
  • 83

1 Answers1

0

applyRefreshToken is async, and therefore returns undefined.

So when your reducer executes:

case REFRESH_TOKEN:
            return applyRefreshToken(state, action);

You actually set your state to undefined which eventually sets this.props to undefined and hence the error.

Either run the async logic and dispatch the new state after getting the response or use thunk / saga / any other middleware which will enable you to have async action creators.

Daniel
  • 6,194
  • 7
  • 33
  • 59
  • Is there any way to made it to async? like async/await statement. – Hide Jul 12 '18 at 12:47
  • It's not enough to return a promise, redux doesn't handle async requests. I personally like [redux-saga](https://github.com/redux-saga/redux-saga) for the use of generators, but thunk might be a bit more intuitive. – Daniel Jul 12 '18 at 12:55
  • Ok I will check it. But, how about made js file for global function and import it? – Hide Jul 12 '18 at 14:10