3

In my React Native app, I have a screen which connects to a server to retrieve an access token; when the token is received, I want to show different content.

So far the component for this screen looks like this:

    import {AppContext} from './stores/app-context';
    import api from '../../api/api';
    
    const MyScreen = () => {
      const {state, dispatch} = React.useContext(AppContext);
    
      const {appToken} = state;
    
      // On opening the screen, get an access token
      useEffect(() => {
        const getToken = async () => {
          try {
            const url = 'url goes here';
            // This should get the token and return it in response.accessToken
            // If the token request is rejected, response.accessToken is null
            const response = await api.getData(`${url}`, {
              headers: {Accept: 'application/json'},
            });
            response.accessToken
              ? dispatch({
                  type: 'setToken',
                  newToken: response.accessToken,
                })
              : dispatch({
                  type: 'setError',
                  text: 'Unable to obtain a token',
                });
          } catch (error) {
            console.log('Error: ', error);
          }
        };
    
        getToken();
      }, []);
    
    
      if (appToken) {
        return <View>Got the token</View>;
      }
    
      return <View>No token yet</View>;
    };
    
    export default MyScreen;

AppContext is defined like this:

    import {AppReducer} from './app-reducer';
    import {IAppAction, IAppState} from './app-types';
    
    let initialState: IAppState = {
      appToken: null,
      errorMessage: null,
    };
    
    const AppContext = createContext<{
      state: IAppState;
      dispatch: React.Dispatch<IAppAction>;
    }>({
      state: initialState,
      dispatch: () => null,
    });
    
    const AppProvider = ({children}: any) => {
      const [state, dispatch] = useReducer(AppReducer, initialState);
    
      return (
        <AppContext.Provider value={{state, dispatch}}>
          {children}
        </AppContext.Provider>
      );
    };
    
    export {AppContext, AppProvider};

`AppReducer` is:
import {IAppAction, IAppState} from './app-types';

const AppReducer = (state: IAppState, action: IAppAction) => {
  switch (action.type) {
    case 'setError':
      if (action.text) {
        return {
          ...state,
          errorMessage: action.text,
        };
      }
    case 'setToken':
      if (action.newToken) {
        return {
          ...state,
          appToken: action.newToken,
        };
      }
      break;
    default:
      throw new Error();
  }
  return {...state};
};
export {AppReducer};

and app-types is:

export interface IAppState {
  appToken: null | string;
  errorMessage: null | string;
}

export type Action = 'setToken' | 'setError';

export interface IAppAction {
  type: Action;
  newToken?: null | string;
  text?: null | string;
}

When I open the screen, the useEffect fires and the token is obtained (I can see it with a console log). Then I assume the dispatch occurs, but the screen doesn't refresh to change from 'No token yet' to 'Got token', as I think it should.

What am I doing wrong here?

Youssouf Oumar
  • 29,373
  • 11
  • 46
  • 65
Sharon
  • 3,471
  • 13
  • 60
  • 93

2 Answers2

2

I have been reading your code, all I could say is to make sure that you have wrapped MyScreen inside AppProvider, like so:

<AppProvider> 
  <MyScreen/>
</AppProvider>
Youssouf Oumar
  • 29,373
  • 11
  • 46
  • 65
1

check out the code sandbox.

Be sure to have the AppProvider and that the api returns the proper format.

Simon Hansen
  • 622
  • 8
  • 15