Search code examples
reactjsreact-nativeasyncstoragejsonparser

How to convert localStorage code in React to AsyncStorage in React Native?


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)))
};


Solution

  • 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
                };
            ...
    }