Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Redux-Saga how do I save my auth token?

I'm using Redux-Saga in a React Native app. When I get the authentication token back from the server, how do I save it to local storage?

I tried using

await AsyncStorage.setItem("token", token);

but React Native complained and said await was a reserved word.

Am I misunderstanding something? Is the saga code not where I should be doing this?

Here is my code

function* loginFlow(action) {
  try {
    let username = action.username;
    let password = action.password;

    const response = yield call(getUser, username, password);
    let token = response.headers.get("access-token");

    const result = yield response.json();

    if (token) {
      console.log("success: ", token);

      yield put({ type: LOGIN_SUCCESS, result });
    } else {
      if (result.error) {
        yield put({ type: LOGIN_FAILURE, error: result.error });
      }
    }
  } catch (e) {
    yield put({ type: LOGIN_FAILURE, error: e.message });
    console.log("error", e);
  }
}

Edit: Here is the getUser function:

const getUser = (username, password) => {
  return fetch(`${apiURL}/${apiVersion}/${apiType}/${apiEndpoint_auth}`, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      email: username,
      password: password
    })
  });
};
like image 602
blueether Avatar asked Mar 19 '18 01:03

blueether


2 Answers

This is how i managed to store token inside redux-saga generator.

function* loginFlow(email, password) {
  try {
    // get your token
    const token = yield call(loginApi, email, password);

    // store token to local storage
    yield call(storeToken, token);

    yield put({ type: LOGIN_SUCCESS });

  } catch (error) {
    yield put({ type: LOGIN_ERROR, error });
  }
}

function loginApi(email, password) {
  return fetch('https://yourApiUrl', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ email, password }),
  })
    .then(response => response.json())
    .then(json => json)
    .catch((error) => {
      throw error;
    });
}

async function storeToken(token) {
      try {
        await AsyncStorage.setItem('token', token);
      } catch (error) {
        console.log('AsyncStorage error during token store:', error);
      }
    }

Note: Store your token before you dispatch your LOGIN_SUCCESS action. So that you will get your token in React Component by re-rendering made by LOGIC_SUCCESS action.

like image 168
Bimal Grg Avatar answered Oct 15 '22 18:10

Bimal Grg


Since each method of the AsyncStorage API returns a Promise object, you could use redux-saga call(fn, ...args) function.

From the documentation of call(fn, ...args), you could use it on a normal function that returns a Promise as a result.

Creates an Effect description that instructs the middleware to call the function fn with args as arguments.

fn: Function - A Generator function, or normal function which either returns a Promise as result, or any other value.

args: Array - An array of values to be passed as arguments to fn

In this case, we could use yield call(fn, ...args) this way:

yield call(AsyncStorage.setItem, "token", token)

This would have the same effect as await, where it would block the execution until the Promise is resolved / rejected.

Full code snippet with minor comments:

function* loginFlow(action) {
    try {
        let username = action.username;
        let password = action.password;

        const response = yield call(getUser, username, password);
        let token = response.headers.get("access-token");

        const result = yield response.json();

        if (token) {
            console.log("success: ", token);

            // Wait / block until the Promise is resolved
            yield call(AsyncStorage.setItem, "token", token);

            // Will be only executed once the previous line have been resolved
            yield put({ type: LOGIN_SUCCESS, result });
        } else {
            if (result.error) {
                yield put({ type: LOGIN_FAILURE, error: result.error });
            }
        }
    } catch (e) {
        yield put({ type: LOGIN_FAILURE, error: e.message });
        console.log("error", e);
    }
}

Reference:

  • https://redux-saga.js.org/docs/api/#callfn-args
like image 40
Adrian Avatar answered Oct 15 '22 16:10

Adrian