Home > Mobile >  React dispatch action updates state after second click
React dispatch action updates state after second click

Time:07-07

I have got my own AuthContext and one of its method is signUp Here is the reducer

const authReducer = (state, action) => {
    switch (action.type) {
        case 'signIn':
            return {errorMessage: '', token: action.payload.token}
        case 'facebookSignIn':
            return {errorMessage: '', token: action.payload.token}
        case 'clear_error_message':
            return {...state, errorMessage: ''}
        case 'signOut':
            return {token: null, errorMessage: ''}
        case 'add_error':
            return {...state, errorMessage: action.payload}
        default:
            return state
    }
}

 const signUp = dispatch => async ({name, email, password}) => {
        try {
            const facebookId = null, age = null, roles = ['ROLE_USER']
            const response = await request.post(
                '/auth/register',
                {facebookId, name, age, email, password, roles}
            )
    
            await SecureStore.setItemAsync('token', response.data.accessToken)
            await SecureStore.setItemAsync('user_email', email)
    
            dispatch({type: 'signIn', payload: response.data.accessToken})
            navigate('Search')
        } catch (err) {
            dispatch({type: 'add_error', payload: 'SIGN_UP_ERROR'})
        }
    }

// ...other methods

export const {Provider, Context} = createDataContext(
    authReducer,
    {signIn, signOut, signUp, clearErrorMessage, tryLocalSignIn, facebookSignIn},
    {token: null, errorMessage: '', user: {id: -1, email: ''}}
)

And here is my method to signing up in different screen

const {state, signUp, clearErrorMessage} = useContext(AuthContext)
const onRegisterPressed = data => {
        signUp({name: data.name, password: data.password, email: data.email})
.then(() => console.log('INSIDE ', state))
        };

here is the result of console.log('INSIDE', state)

first click:

INSIDE   Object {
  "errorMessage": "",
  "token": null,
  "user": Object {
    "email": "",
    "id": -1,
  },
}

second click:

INSIDE   Object {
  "errorMessage": "SIGN_UP_ERROR",
  "token": null,
  "user": Object {
    "email": "",
    "id": -1,
  },
}

createDataContext file

import React, {useReducer} from 'react'

export default (reducer, actions, defaultValue) => {
    const Context = React.createContext()

    const Provider = ({children}) => {
        const [state, dispatch] = useReducer(reducer, defaultValue)

        const boundActions = {}
        for (let key in actions) {
            boundActions[key] = actions[key](dispatch)
        }

        return (
            <Context.Provider value={{state, ...boundActions}}>
                {children}
            </Context.Provider>
        )
    }

    return {Context, Provider}
}

and the problem is that the state is updated after the second click on register button. First click returns the empty errorMessage while it should returns SIGN_UP_ERROR. The same problem was with signIn action. Generally, the state is updated after second click.

How to fix this?

CodePudding user response:

Thanks for more insight. As I can see you're checking context state value in the promise.then, but I think it's not gonna be there at this point of time, because promise.then is triggered just after the call. At this point of time state is not updated. On second click it's already updated, that's why It's working. I'm not sure what do you want to achieve here, but I would suggest rather not use promise.then if you are managing state in the context. I would use state from the context. I think this should work.

const { state, signUp, clearErrorMessage } = useContext(AuthContext);
const onRegisterPressed = (data) => {
  signUp({ name: data.name, password: data.password, email: data.email });
};
console.log(state);

If you want to have value in the promise you will need to return it in the implementation of signUp function.

const signUp = (dispatch) => async ({ name, email, password }) => {
  try {
    const facebookId = null,
      age = null,
      roles = ["ROLE_USER"];
    const response = await request.post("/auth/register", {
      facebookId,
      name,
      age,
      email,
      password,
      roles
    });

    await SecureStore.setItemAsync("token", response.data.accessToken);
    await SecureStore.setItemAsync("user_email", email);

    dispatch({ type: "signIn", payload: response.data.accessToken });
    navigate("Search");
    return { type: "signIn", payload: response.data.accessToken };
  } catch (err) {
    dispatch({ type: "add_error", payload: "SIGN_UP_ERROR" });
    return { type: "add_error", payload: "SIGN_UP_ERROR" };
  }
};

Then in the call

const { state, signUp, clearErrorMessage } = useContext(AuthContext);
const onRegisterPressed = (data) => {
  signUp({
    name: data.name,
    password: data.password,
    email: data.email
  }).then((promiseState) => console.log("INSIDE ", promiseState));
};
  • Related