Search code examples
reactjsaxiosredux-sagaredux-toolkit

Redux Saga call not taking localStorage tokens


I have recently started learning Redux Saga and Redux Toolkit and I have come across a redux-saga and toolkit problem.

I am trying to make a login system that places the tokens in your system and then whenever the app loads for the first time it fetches the user details from the database, if it returns a 401, then the user isnt logged in and I set the return as undefined. This gives me the ability to hide components that arent supposed to be visible. Is this the best direction to take it, do you have any suggestions?

It appears that the data fetched after Axios and placed into the localStorage is not used by a handler? Is it doing the next in line tasks asynchronously? Would I have to use an actionChannel there? If so how would I implement it, it states 'pattern' but it becomes very confusing with what that is since they dont use redux-toolkit with createSlices and use redux only?

from login data gets submitted through formData, the watcher saga sees that I have hit logInUser and sends it to the right handler. The handler then takes the data, sends it through axios using the yield call(requestLogInUser) with the action as payload. The response from axios is then placed in res, from which the two tokens are extracted and set in local storage (which works).

After which the yield call(handleGetCurrentUser); should start the getCurrentUser handler and actually get the user details data from the database using the tokens set in localStorage.

However what happens is that the data does not get called instead it just gives me 401, meaning it didnt find/try to grab the tokens from localStorage even though axios asks for it. This makes me thing its because its asynchronous and firing off the yield call somehow before the tokens are set? I could not find it trying the action at all using the redux dev tool, but I noticed it firing it off because of console.log and it returning the 401 on that api endpoint.

My submit button in Login.js to show the dispatch of formData

  const handleSubmit = (e) => {
    e.preventDefault();
    dispatch(logInUser(formData));
  };

My User.js file with all the handlers:

export function* handleLogInUser(action) {
  try {
    const res = yield call(requestLogInUser, action.payload);
    yield localStorage.setItem("access_token", res.data.access_token);
    yield localStorage.setItem("refresh_token", res.data.refresh_token);
    yield call(handleGetCurrentUser);
  } catch (error) {
    console.log(error);
  }
}

export function* handleGetCurrentUser() {
  try {
    console.log("in request get user");
    const response = yield call(requestGetCurrentUser);
    const { data } = response;
    yield put(setCurrentUser({ ...data }));
  } catch (error) {
    console.log(error);
  }
}

export function* handleSetCurrentUser(data) {
  try {
    console.log("in request set user");
    yield put(setCurrentUser({ data }));
  } catch (error) {}
}

My user.js requests file which contains the Axios requests

export function requestLogInUser(formData) {
  return axiosLogInInstance.post(`auth/token/`, {
    grant_type: "password",
    username: formData.email,
    password: formData.password,
    client_id: "asdfasdfasdfdfsdfassfsfWJFdQ",
  });
}

export function requestGetCurrentUser() {
  return axiosInstance.get("/user/currentUser/");
}

My axiosInstance

const baseURL = "http://127.0.0.1:8000/api/";

const axiosInstance = axios.create({
  baseURL: baseURL,
  timeout: 5000,
  headers: {
    Authorization: "Bearer " + localStorage.getItem("access_token"),
    "Content-Type": "application/json",
    accept: "application/json",
  },
});

My UserSlice.js file that contains the slice

const userSlice = createSlice({
  name: "user",
  initialState: {},
  reducers: {
    logInUser(state, action) {},
    getCurrentUser() {},
    setCurrentUser(state, action) {
      const userData = action.payload;
      return { ...state, ...userData };
    },
  },
});

finally Rootsaga file

export function* watcherSaga() {
  //authentication/user
  yield takeLeading(logInUser.type, handleLogInUser);
  yield takeLatest(getCurrentUser.type, handleGetCurrentUser);
}

Solution

  • your code about sagas and react is right, the problem is the axiosInstance, in the code, axios only get the localstorage one times when the instance be created, add the axios request interceptors

    axiosInstance.interceptors.request(config => {
      config.headers.Authorization = "Bearer " + localStorage.getItem("access_token")
      return config
    }, null)