Avoiding require cycle (refresh auth token at redux middleware)

Viewed 148

Would be glad to get an idea of how to destruct my code for avoiding a require cycle (I'm not encountering any bug or so, just want to make my code the best possible, w/t memory leaking, encounter unset vars or damage performance/readability)

My hierarchy looks like so:

 services
 -- api.ts
 -- userService.ts
 store
 -- actions
 ---- auth.ts
 -- index.tsx
 -- jwt.ts

At store/index.tsx I'm importing store/jwt.ts for -

const store = createStore(reducer, compose(applyMiddleware(thunk, jwt)))

While at store/jwt.ts I'm importing actions/auth.ts for -

import { refreshToken as refreshTokenFunc } from './actions/auth'

const jwt: Middleware = ({ dispatch, getState }: { dispatch; getState: () => IRootState }) => {
    return (next) => (action) => {
        const nextAction = () => next(action)
            if (should refresh token) {
                return !getState().auth.refreshTokenPromise
                    ? refreshTokenFunc(dispatch).then(nextAction)
                    : getState().auth.refreshTokenPromise.then(nextAction)
            }
        return nextAction()
    }
}

(While I'm having refreshTokenPromise as a redux variable for avoiding calling refresh token endpoint a few times in a row but waiting for the same call to end.)

refreshTokenFunc from actions/auth.ts uses userService.refreshToken (and imports userService...)

export async function refreshToken(dispatch) {
    const handleErr = () => Promise.reject(dispatch(logout()))
    
    const refreshTokenPromise = userService
        .refreshToken(... some params)
        .then(({ status, ...rest }) => {
            status
                ? dispatch(...)
                : handleErr()
        })
        .catch(handleErr)
    await dispatch({
        type: AUTH_REFRESH_TOKEN,
        refreshTokenPromise
    })
    return refreshTokenPromise
}

services/userService.ts imports main services/api.ts service

import API from './api'

refreshToken: (params) =>
        API.post(EEndPoints.REFRESH_TOKEN, ...params)

and services/api.ts imports store/index.tsx for common getState either dispatch common things as

store.dispatch({ type: GENERAL_TOGGLE_NO_INTERNET })
store.dispatch({ type: AUTH_LOGOUT }) // in case token sent returned as invalid by API...)

to sum up -

services/userService.ts -> services/api.ts -> store/index.tsx -> store/jwt.ts -> store/actions/auth.ts -> services/userService.ts

I'm sure I'm not the first one using middleware for refreshing tokens, having this kind of hierarchy, or using a main api service with common dispatch or getState, so maybe someone found a way around this loop. Any time I try to change some of the imports I still have this loop just from a different file to a different file (for example from store/actions/auth.ts to itself back)

I tried to keep the code as minimal as possible, in case it's not clear enough please comment.

Any idea/thought will be very appreciated! :)

0 Answers
Related