3

I want to know how to convert my React code to React Native using AsyncStorage. I have tried the below code but I am getting "undefined" for "isAuthenticated" and also an "error" in "JSON.parse(AsyncStorage.getItem('token')". I want to use the "isAuthenticated" value to show only a particular part of a Component.

React Code:

export const Auth = (state = {
        isLoading: false,
        isAuthenticated: localStorage.getItem('token') ? true : false,
        token: localStorage.getItem('token'),
        user: localStorage.getItem('creds') ? JSON.parse(localStorage.getItem('creds')) : null,
        errMess: null
    }, action) => {
    switch (action.type) {
        case ActionTypes.LOGIN_REQUEST:
            return {...state,
                isLoading: true,
                isAuthenticated: false,
                user: action.creds
            };
        case ActionTypes.LOGIN_SUCCESS:
            return {...state,
                isLoading: false,
                isAuthenticated: true,
                errMess: '',
                token: action.token
            };
        case ActionTypes.LOGIN_FAILURE:
            return {...state,
                isLoading: false,
                isAuthenticated: false,
                errMess: action.message
            };
        case ActionTypes.LOGOUT_REQUEST:
            return {...state,
                isLoading: true,
                isAuthenticated: true
            };
        case ActionTypes.LOGOUT_SUCCESS:
            return {...state,
                isLoading: false,
                isAuthenticated: false,
                token: '',
                user: null
            };
        default:
            return state
    }
}

My code in React Native:

import * as ActionTypes from './ActionTypes';

// The auth reducer. The starting state sets authentication
// based on a token being in local storage. In a real app,
// we would also want a util to check if the token is expired.
export const auth = (state = {
  isLoading: false,
  isAuthenticated: false,
  token: null,
  user: null,
  errMess: null
}, action) => {
  switch (action.type) {
    case ActionTypes.GET_AUTH:
      return {
        ...state,
        ...action.payload
      };
    case ActionTypes.LOGIN_REQUEST:
      return {
        ...state,
        isLoading: true,
        isAuthenticated: false,
        user: action.creds
      };
    case ActionTypes.LOGIN_SUCCESS:
      return {
        ...state,
        isLoading: false,
        isAuthenticated: true,
        errMess: '',
        token: action.token
      };
    case ActionTypes.LOGIN_FAILURE:
      return {
        ...state,
        isLoading: false,
        isAuthenticated: false,
        errMess: action.message
      };
    case ActionTypes.LOGOUT_REQUEST:
      return {
        ...state,
        isLoading: true,
        isAuthenticated: true
      };
    case ActionTypes.LOGOUT_SUCCESS:
      return {
        ...state,
        isLoading: false,
        isAuthenticated: false,
        token: '',
        user: null
      };
    default:
      return state
  }
}

Thanks in advance.

Edit: This my updated code.

export const getAuth = () => {
  return async(dispatch) => {
    const [token, creds] = await Promise.all([
      AsyncStorage.getItem('token'),
      AsyncStorage.getItem('creds')
    ])

    dispatch({
      type: ActionTypes.GET_AUTH,
      payload: {
        isLoading: false,
        isAuthenticated: !!token,
        token: token,
        user: JSON.parse(creds),
        errMess: null
      }
    });
  }
}

export const loginUser = (creds) => (dispatch) => {
  // We dispatch requestLogin to kickoff the call to the API
  dispatch(requestLogin(creds))

  return fetch(baseUrl + 'users/login', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(creds)
    })
    .then(response => {
        if (response.ok) {
          return response;
        } else {
          var error = new Error('Error ' + response.status + ': ' + response.statusText);
          error.response = response;
          throw error;
        }
      },
      error => {
        throw error;
      })
    .then(response => response.json())
    .then(response => {
      if (response.success) {
        async() => {
          try {
            await AsyncStorage.setItem('token', response.token);
            await AsyncStorage.setItem('creds', JSON.stringify(creds));
          } catch (error) {
            // Error saving data
          }
        }
        // If login was successful, set the token in local storage
        //AsyncStorage.setItem('token', response.token);
        //AsyncStorage.setItem('creds', JSON.stringify(creds));
        // Dispatch the success action
        dispatch(fetchSaves());
        dispatch(receiveLogin(response));
      } else {
        var error = new Error('Error ' + response.status);
        error.response = response;
        throw error;
      }
    })
    .catch(error => dispatch(loginError(error.message)))
};

3 Answers 3

2

I think you should leave initialState with default values, use redux-thunk and dispatch an action at the start of the app to get auth data.

const getAuth = () => {
    return async (dispatch) => {
        const [token, user] = await Promise.all([
          AsyncStorage.getItem('token'),
          AsyncStorage.getItem('creds')
        ])

        dispatch({
          type: 'GET_AUTH',
          payload: {
            isLoading: false,
            isAuthenticated: !!token,
            token,
            user: JSON.parse(creds),
            errMess: null
          }
        });
    }
}

const App = connect({}, {getAuth})(({getAuth}) => {

  useEffect(() => {
    getAuth();
  }, [])
  
  return ...
})

export const auth = async (state = {
    isLoading: false,
    isAuthenticated: false,
    token: null,
    user: null,
    errMess: null
}, action) => {
    switch (action.type) {
        case ActionTypes.GET_AUTH:
            return {
                ...state,
                ...action.payload
            };
        ...
}
Sign up to request clarification or add additional context in comments.

3 Comments

It worked!! But I had to make a change in the auth file - I had to remove ...action.payload in ActionTypes.GET_AUTH, only leaving ...state. I don't understand why but it works after the change
Can you console.log(action.payload) before returning the state?
It works perfectly. Even with ...payload. Thanks a lot!!
0

AsyncStorage is asynchronous. Use

const value = await AsyncStorage.getItem('token');

2 Comments

I tried the above solution but got an error saying "Can not use keyword 'await' outside an async function"
So make your function async
0

@Samir Kumar try this

import { AsyncStorage } from 'react-native';

async functionName(){
  const token = await AsyncStorage.getItem('token');
}

1 Comment

I have tried the above. It seems there is a problem in saving the token in AsyncStorage and thus it is returning a null value. Could you please check my loginUser function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.