title: Infinite loop on Redux saga yield call using Axios with JWT tokens
body: <p>I have been trying to obtain data using Axios through Redux-saga using Redux-toolkit &amp; react. It appears that intercepting a saga call with a token gets redux-saga in an infinite loop? Or is it because of my watchers?</p>
  <p>I have recently been learning how to program so my skills in all areas are not yet great, hope you dont mind the way the code is written as I have been following tutorials mostly.</p>
  <p>On handleSubmit from a <strong>Header.tsx</strong> to <strong>dispatch</strong></p>
  <pre class="lang-js prettyprint-override"><code>const handleSubmit = (e) =&gt; {
    e.preventDefault();
    dispatch(getCurrentUser());
  };
  </code></pre>
  <p>my <strong>rootSaga.tsx</strong> includes all watcherSagas notices the dispatch for <strong>getCurrentUser()</strong></p>
  <pre class="lang-js prettyprint-override"><code>import { takeLatest } from &quot;redux-saga/effects&quot;;
  import {
    handleLogInUser,
    handleGetCurrentUser,
    handleSetCurrentUser,
  } from &quot;./handlers/user&quot;;
  import {
    logInUser,
    getCurrentUser,
    setCurrentUser,
  } from &quot;../slices/user/userSlice&quot;;
  
  export function* watcherSaga() {
    yield takeLatest(logInUser.type, handleLogInUser);
    yield takeLatest(getCurrentUser.type, handleGetCurrentUser);
    yield takeLatest(setCurrentUser.type, handleSetCurrentUser);
  }
  </code></pre>
  <p>the watcher calls <strong>handleGetCurrentUser</strong> for the saga located in <strong>user.tsx file in handler</strong> folder:</p>
  <pre class="lang-js prettyprint-override"><code>import { call, put } from &quot;redux-saga/effects&quot;;
  import { setCurrentUser } from &quot;../../slices/user/userSlice&quot;;
  import { requestLogInUser, requestGetCurrentUser } from &quot;../requests/user&quot;;
  
  export function* handleLogInUser(action) {
    try {
      console.log(action + &quot;in handleLogInUser&quot;);
      yield call(requestLogInUser(action));
    } catch (error) {
      console.log(error);
    }
  }
  
  export function* handleGetCurrentUser(action) {
    try {
      const response = yield call(requestGetCurrentUser);
      const userData = response;
      yield put(setCurrentUser({ ...userData }));
    } catch (error) {
      console.log(error);
    }
  }
  </code></pre>
  <p>Which then uses yield call to <strong>requestGetCurrentUser</strong> which fires off the request to the following <strong>user.tsx in requests</strong> folder</p>
  <pre class="lang-js prettyprint-override"><code>import axiosInstance from &quot;../../../axios/Axios&quot;;
  
  export function requestGetCurrentUser() {
    return axiosInstance.request({ method: &quot;get&quot;, url: &quot;/user/currentUser/&quot; });
  }
  </code></pre>
  <p>The response is given back and put in const userData, I consoleLog()'d the handler and discovered the following:</p>
  <ol>
  <li>it will reach the handler successfully</li>
  <li>go to the yield call</li>
  <li>obtain the data successfully</li>
  <li>return the data back to the handler</li>
  <li>then it restarts the entire yield call again?</li>
  </ol>
  <p>It also never makes it back to the userSlice in order to put the data.</p>
  <p><strong>axiosInstance</strong> in my <strong>axios.tsx</strong> file which includes the interceptor and gets the access_token and adds it to the header.</p>
  <pre class="lang-js prettyprint-override"><code>import axios from &quot;axios&quot;;
  
  const baseURL = &quot;http://127.0.0.1:8000/api/&quot;;
  
  const axiosInstance = axios.create({
    baseURL: baseURL,
    timeout: 5000,
    headers: {
      Authorization: &quot;Bearer &quot; + localStorage.getItem(&quot;access_token&quot;),
      &quot;Content-Type&quot;: &quot;application/json&quot;,
      accept: &quot;application/json&quot;,
    },
  });
  
  axiosInstance.interceptors.response.use(
    (response) =&gt; {
      return response;
    },
    async function (error) {
      const originalRequest = error.config;
  
              if (typeof error.response === &quot;undefined&quot;) {
        alert(
          &quot;A server/network error occurred. &quot; +
            &quot;Looks like CORS might be the problem. &quot; +
            &quot;Sorry about this - we will get it fixed shortly.&quot;
        );
        return Promise.reject(error);
      }
  
      if (
        error.response.status === 401 &amp;&amp;
        originalRequest.url === baseURL + &quot;token/refresh/&quot;
      ) {
        window.location.href = &quot;/login/&quot;;
        return Promise.reject(error);
      }
  
      if (
        error.response.data.code === &quot;token_not_valid&quot; &amp;&amp;
        error.response.status === 401 &amp;&amp;
        error.response.statusText === &quot;Unauthorized&quot;
      ) {
        const refreshToken = localStorage.getItem(&quot;refresh_token&quot;);
  
        if (refreshToken) {
          const tokenParts = JSON.parse(atob(refreshToken.split(&quot;.&quot;)[1]));
  
          // exp date in token is expressed in seconds, while now() returns milliseconds:
          const now = Math.ceil(Date.now() / 1000);
          console.log(tokenParts.exp);
  
          if (tokenParts.exp &gt; now) {
            return axiosInstance
              .post(&quot;/token/refresh/&quot;, {
                refresh: refreshToken,
              })
              .then((response) =&gt; {
                localStorage.setItem(&quot;access_token&quot;, response.data.access);
                localStorage.setItem(&quot;refresh_token&quot;, response.data.refresh);
  
                axiosInstance.defaults.headers[&quot;Authorization&quot;] =
                  &quot;JWT &quot; + response.data.access;
                originalRequest.headers[&quot;Authorization&quot;] =
                  &quot;JWT &quot; + response.data.access;
  
                return axiosInstance(originalRequest);
              })
              .catch((err) =&gt; {
                console.log(err);
              });
          } else {
            console.log(&quot;Refresh token is expired&quot;, tokenParts.exp, now);
            window.location.href = &quot;/login/&quot;;
          }
        } else {
          console.log(&quot;Refresh token not available.&quot;);
          window.location.href = &quot;/login/&quot;;
        }
      }
  
      // specific error handling done elsewhere
      return Promise.reject(error);
    }
  );
  
  export default axiosInstance;
  </code></pre>
  <p>The <strong>userSlice.tsx</strong></p>
  <pre class="lang-js prettyprint-override"><code>import { createSlice } from &quot;@reduxjs/toolkit&quot;;
  
  const userSlice = createSlice({
    name: &quot;user&quot;,
    initialState: {},
    reducers: {
      logInUser(state, action) {},
      getCurrentUser() {},
      setCurrentUser(state, action) {
        const userData = action.payload;
        console.log(userData + &quot;we are now back in slice&quot;);
        return { ...state, ...userData };
      },
    },
  });
  
  export const { logInUser, getCurrentUser, setCurrentUser } = userSlice.actions;
  
  export default userSlice.reducer;
  </code></pre>
  <p>I discovered that if I were to remove the authorization token it only fires off once and gets out of the infinite loop since it throws the unauthorised error.</p>
  <p>Any suggestions would be greatly appreciated, thanks!</p>