Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use react-redux to store a token and use it on multiple axios request?

I am building an app using react native that requires me to do multiple get request on same API with token.

Let say the url is like this

Token URL = https://test.co/v1/tokens, API URL 1 = https://test.co/v1/students and API URL 2 = https://test.co/v1/cars

First of all, to fetch the data from either API URLs, I wrote it like this

Example for students_actions.js

import axios from 'axios';
import { FETCH_STUDENT } from './types';

const TOKEN_URL = '...'
const STUDENT_URL = '...'

export const fetchStudent = (callback) => async (dispatch) => {
    axios.post(TOKEN_URL, {
        email: 'email',
        password: 'password',
        role: 'user'
    })
    .then((response) => {
        const accessToken = response.data.token;
        //console.log(accessToken);
        axios.get(STUDENT_URL, {
            headers: { 'Authorization': 'Bearer '.concat(accessToken) }
        })
        .then((studentResponse) => {
            dispatch({ type: FETCH_STUDENT, payload: studentResponse.data });
            callback();
        })
        .catch((e) => {
            console.log(e);
        });
    })
    .catch((error) => {
        console.log(error);
    });
};

Example for students_reducers.js

import { FETCH_STUDENT } from '../actions/types';

const INITIAL_STATE = {
    data: []
};

export default function (state = INITIAL_STATE, action) {
    switch (action.type) {
        case FETCH_STUDENT:
            return action.payload;
        default:
            return state;
    }
}

and call it inside render function like this

//some code
import { connect } from 'react-redux';

import * as actions from '../actions';

onButtonPressProfile = () => {
    this.props.fetchStudent(() => {
        this.props.navigation.navigate('Profile');
    });
}
class StudentProfile extends Component {
    render() {
        return(
            <View><Text>{this.props.students.name}</Text></View>
        );
    }
}

function mapStateToProps({ students }) {
    return { students: students.data };
}

export default connect(mapStateToProps, actions)(StudentProfile);

While this is all running without any problem I feel like students_actions.js can be further simplify by writing the code for retrieving the token in other file and call the value back inside students_actions.js for GET request.

The reason is so I do not have to request token everytime I want to access either students or cars. Lets say, I did request one time and I can use the same token for like 24 hours to access the API. Once it expired, then I have to do another request for token to access the API again.

I already wrote the code for token_actions.js together with token_reducer.js. Below are the two codes.

token_actions.js

//import library
// this code works
const TOKEN_URL = apiConfig.url + 'tokens';
const auth = {
    email: 'email',
    password: 'password',
    role: 'user'
};

export const fetchToken = () => async (dispatch, getState) => {
        axios.post(TOKEN_URL, auth)
        .then((response) => {

            dispatch({ type: FETCH_TOKEN, payload: response.data.token });
        })
        .catch((error) => {
            console.log(error);
        });
};

token_reducer.js

import {
    FETCH_TOKEN
} from '../actions/types';

const INITIAL_STATE = {
    data: []
};

export default function (state = INITIAL_STATE, action) {
    switch (action.type) {
        case FETCH_TOKEN:
            return action.payload;
        default:
            return state;
}

}

students_actions.js

axios.get(STUDENT_URL, { headers: {
                           'Authorization': 'Bearer '.concat(here is the value from token_actions)}})

And now I am stuck at how should I call/import the payload from token_actions.js into students_actions.js? Should I use mapStateToProps or is there any other way to do this?

Right now, this app does not have any authentication function yet. It is basically an app that shows the data fetched from API.

I wrote this app mainly based on examples I found online and for this case I found this example but seems not really what I want to achieve.

I do not really quite understand javascript so I will be really glad if anyone could pointed out any link related to this case or maybe same questions here on Stackoverflow and also maybe some suggestions.

Thank you.

like image 851
Fang Avatar asked Nov 15 '17 08:11

Fang


People also ask

Can I store auth token in Redux?

Better you should save the token in local storage and pull in redux store as initial value so it automatically save with every reload. Show activity on this post. You should never store JWT tokens inside your localStorage since they are vulnerable to client side attacks like XSS or CSRF attacks.


1 Answers

I the logical thing to do would be to create something like an AuthReducer where you store your token and your refresh token. This is an example of my basic AuthReducer:

export const INITIAL_STATE = {
  oAuthToken: '',
  refreshToken: '',
};

export default AuthReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case REFRESH_OAUTH_DATA:
      const { oAuthToken, refreshToken } = action.payload;
      return { ...state, oAuthToken, refreshToken };

    case LOGOUT:
      return INITIAL_STATE;

    case LOGIN_FETCH_SUCCESS:
      const { oAuthToken, refreshToken } = action.payload;
      return { ...state, oAuthToken, refreshToken };

    default:
      return state;
  }
};

Now you can get your token in your actions using the getState method doing something like:

export const fetchStudent = (callback) => async (dispatch, getState) => {
    const token = getState().AuthReducer.oAuthToken;
    ....
};

Remember that if you are using ES6 you might also want to use await:

export const fetchStudent = (callback) => async (dispatch, getState) => {
    try {
        const accessToken = getState().AuthReducer.oAuthToken;

        let response = await axios.get(STUDENT_URL, {
            headers: { 'Authorization': 'Bearer '.concat(accessToken) }
         })

         dispatch({ type: FETCH_STUDENT, payload: response.data });
         callback();
    } catch(e) {
        console.log(e);
    }
};

This way your code is way easier to read and maintain.

like image 74
EnriqueDev Avatar answered Oct 15 '22 16:10

EnriqueDev