2017-10-15 4 views
0

J'ai un middleware qui peut aller au jeton d'actualisation avant l'exécution de l'action suivante, puis exécuter l'autre action à l'expiration du jeton d'accès. Mais si je fais plus d'une demande à la fois et que le jeton d'accès est terminé, j'essaie d'obtenir autant de jetons d'actualisation que je le demande. Je vérifie la propriété isLoading dans l'état pour empêcher ceci. Mais après la requête, la valeur de isLoading est vraie dans le réducteur, elle semble être fausse dans le middleware, donc elle demande encore et encore.redux refresh middleware de jeton

J'envoie refreshTokenPromise dans l'action fetching_refresh_token, mais je n'obtiens jamais state.refreshTokenPromise, il est toujours indéfini.

J'ai vraiment un problème avec l'état.

Donc, voici ma question, comment puis-je accéder à la valeur de changement d'état dans le middleware?

Refresh middleware jeton: (cette version touche le point final à plusieurs reprises)

import { AsyncStorage } from 'react-native'; 
import { MIN_TOKEN_LIFESPAN } from 'react-native-dotenv'; 
import moment from 'moment'; 
import Api from '../lib/api'; 
import { 
    FETCHING_REFRESH_TOKEN, 
    FETCHING_REFRESH_TOKEN_SUCCESS, 
    FETCHING_REFRESH_TOKEN_FAILURE } from '../actions/constants'; 

export default function tokenMiddleware({ dispatch, getState }) { 
    return next => async (action) => { 
    if (typeof action === 'function') { 
     const state = getState(); 
     if (state) { 
     const expiresIn = await AsyncStorage.getItem('EXPIRES_IN'); 
     if (expiresIn && isExpired(JSON.parse(expiresIn))) { 
      if (!state.refreshToken.isLoading) { 
      return refreshToken(dispatch).then(() => next(action)); 
      } 
      return state.refreshTokenPromise.then(() => next(action)); 
     } 
     } 
    } 
    return next(action); 
    }; 
} 

async function refreshToken(dispatch) { 
    const clientId = await AsyncStorage.getItem('CLIENT_ID'); 
    const clientSecret = await AsyncStorage.getItem('CLIENT_SECRET'); 
    const refreshToken1 = await AsyncStorage.getItem('REFRESH_TOKEN'); 

    const userObject = { 
    grant_type: 'refresh_token', 
    client_id: JSON.parse(clientId), 
    client_secret: JSON.parse(clientSecret), 
    refresh_token: refreshToken1, 
    }; 

    const userParams = Object.keys(userObject).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(userObject[key])).join('&'); 

    const refreshTokenPromise = Api.post('/token', userParams).then(async (res) => { 
    await AsyncStorage.setItem('ACCESS_TOKEN', res.access_token); 
    await AsyncStorage.setItem('REFRESH_TOKEN', res.refresh_token); 
    await AsyncStorage.setItem('EXPIRES_IN', JSON.stringify(res['.expires'])); 

    dispatch({ 
     type: FETCHING_REFRESH_TOKEN_SUCCESS, 
     data: res, 
    }); 

    return res ? Promise.resolve(res) : Promise.reject({ 
     message: 'could not refresh token', 
    }); 
    }).catch((err) => { 
    dispatch({ 
     type: FETCHING_REFRESH_TOKEN_FAILURE, 
    }); 

    throw err; 
    }); 

    dispatch({ 
    type: FETCHING_REFRESH_TOKEN, 
    refreshTokenPromise, 
    }); 

    return refreshTokenPromise; 
} 

function isExpired(expiresIn) { 
    return moment(expiresIn).diff(moment(), 'seconds') < MIN_TOKEN_LIFESPAN; 
} 

Refresh réducteur jeton:

import { 
    FETCHING_REFRESH_TOKEN, 
    FETCHING_REFRESH_TOKEN_SUCCESS, 
    FETCHING_REFRESH_TOKEN_FAILURE } from '../actions/constants'; 

const initialState = { 
    token: [], 
    isLoading: false, 
    error: false, 
}; 

export default function refreshTokenReducer(state = initialState, action) { 
    switch (action.type) { 
    case FETCHING_REFRESH_TOKEN: 
     return { 
     ...state, 
     token: [], 
     isLoading: true, 
     }; 
    case FETCHING_REFRESH_TOKEN_SUCCESS: 
     return { 
     ...state, 
     isLoading: false, 
     token: action.data, 
     }; 
    case FETCHING_REFRESH_TOKEN_FAILURE: 
     return { 
     ...state, 
     isLoading: false, 
     error: true, 
     }; 
    default: 
     return state; 
    } 
} 

En attendant, quand je l'envoie à la getState à la fonction refreshToken, J'arrive à la valeur de changement d'état dans le refreshToken. Mais dans cette version, le jeton d'actualisation va à d'autres actions sans être actualisé.

Singe version patchée: (cette version ne fait que 1 demande)

import { AsyncStorage } from 'react-native'; 
import { MIN_TOKEN_LIFESPAN } from 'react-native-dotenv'; 
import moment from 'moment'; 
import Api from '../lib/api'; 
import { 
    FETCHING_REFRESH_TOKEN, 
    FETCHING_REFRESH_TOKEN_SUCCESS, 
    FETCHING_REFRESH_TOKEN_FAILURE } from '../actions/constants'; 

export default function tokenMiddleware({ dispatch, getState }) { 
    return next => async (action) => { 
    if (typeof action === 'function') { 
     const state = getState(); 
     if (state) { 
     const expiresIn = await AsyncStorage.getItem('EXPIRES_IN'); 
     if (expiresIn && isExpired(JSON.parse(expiresIn))) { 
      if (!state.refreshTokenPromise) { 
      return refreshToken(dispatch, getState).then(() => next(action)); 
      } 
      return state.refreshTokenPromise.then(() => next(action)); 
     } 
     } 
    } 
    return next(action); 
    }; 
} 

async function refreshToken(dispatch, getState) { 
    const clientId = await AsyncStorage.getItem('CLIENT_ID'); 
    const clientSecret = await AsyncStorage.getItem('CLIENT_SECRET'); 
    const refreshToken1 = await AsyncStorage.getItem('REFRESH_TOKEN'); 

    const userObject = { 
    grant_type: 'refresh_token', 
    client_id: JSON.parse(clientId), 
    client_secret: JSON.parse(clientSecret), 
    refresh_token: refreshToken1, 
    }; 

    if (!getState().refreshToken.isLoading) { 
    const userParams = Object.keys(userObject).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(userObject[key])).join('&'); 

    const refreshTokenPromise = Api.post('/token', userParams).then(async (res) => { 
     await AsyncStorage.setItem('ACCESS_TOKEN', res.access_token); 
     await AsyncStorage.setItem('REFRESH_TOKEN', res.refresh_token); 
     await AsyncStorage.setItem('EXPIRES_IN', JSON.stringify(res['.expires'])); 

     dispatch({ 
     type: FETCHING_REFRESH_TOKEN_SUCCESS, 
     data: res, 
     }); 

     return res ? Promise.resolve(res) : Promise.reject({ 
     message: 'could not refresh token', 
     }); 
    }).catch((err) => { 
     dispatch({ 
     type: FETCHING_REFRESH_TOKEN_FAILURE, 
     }); 

     throw err; 
    }); 

    dispatch({ 
     type: FETCHING_REFRESH_TOKEN, 
     refreshTokenPromise, 
    }); 

    return refreshTokenPromise; 
    } 
} 

function isExpired(expiresIn) { 
    return moment(expiresIn).diff(moment(), 'seconds') < MIN_TOKEN_LIFESPAN; 
} 

Merci.

+0

Je ne vois pas où vous vérifiez la valeur isLoading dans le middleware .. – Freez

+0

J'ai essayé d'autres choses, réparé, regarderiez-vous à nouveau ? – ccoeder

+0

Bien sûr! Im curieux – Freez

Répondre

0

vous pourriez bénéficier de Redux-Sagas

https://github.com/redux-saga/redux-saga

Redux-coureur est juste sagas de fond qui surveille vos actions et peut réagir quand une action spécifique est satisfaite. Vous pouvez écouter toutes les actions et réagir à tout ou vous pouvez réagir à seulement plus tard comme mentionné dans les commentaires

https://redux-saga.js.org/docs/api/#takelatestpattern-saga-args

tout Redux-thunk est juste une autre façon de créer des actions sur la route et attendre un certain I/O pour arriver et ensuite créer plus d'actions quand E/S est fait. C'est plus comme un motif de code synchronisé et redux-sagas ressemble plus à un multi-thread. Sur le thread principal, vous avez votre application en cours d'exécution et sur le thread d'arrière-plan vous avez des moniteurs sagas et des réactions