3

My reducer:

export const initialUserState = {
    username: '',
    address: '',
    token: '',
    address: '',
    loggedIn: false,
    loaded: false,
};

export const userReducer = async (state, action) => {
    try {
        switch (action.type) {
            case 'LOAD':
                try {
                    const value = JSON.parse(await AsyncStorage.getItem('user_info'));
                    const newState = { ...state, ...value, loggedIn: true, loaded: true };
                    console.log('New State:', newState);

                    if (value !== null) {
                        return newState;
                    }
                } catch (error) {
                    return { ...state, loaded: true };
                }
                break;
            default:
                return state;
        }
    } catch (error) {
        console.log(error);
        return state;
    }
};

My App.js:

import { userReducer, initialUserState } from './reducer';

const App = () => {
    const [user, dispatch] = useReducer(userReducer, initialUserState);

    useEffect(() => {
        dispatch({ type: 'LOAD', ready: setReady });
    }, []);

    useEffect(() => {
        console.log('state user:', user);
    }, [user]);
}

export default App;

What happens is that in App.js, after the second useEffect is called, the state updates and user returns a promise. The promise has multiple properties, inside one of them is the correct state it was supposed to return. Shouldn't it return the state? Am I doing something wrong?

In a nutshell:

1) App starts

2) useEffect calls the first time and has the correct initial state

3) I call the LOAD action which will update the state

4) useEffect gets called the second time. This time it returns a promise, and it should return only the updated state of the store, I guess?

Console

bruxo00
  • 135
  • 1
  • 2
  • 12
  • You would use redux-thunk for async operations and move your request to a action creator https://github.com/reduxjs/redux-thunk – Gabriel Martinez Bustos Apr 16 '20 at 23:20
  • 1
    So this question is asking about React Hooks' "reducers" and not redux, right? Can you use redux-thunk with react hooks? – WillD Apr 16 '20 at 23:27

2 Answers2

9

It looks like the function you pass to useReducer is async. All async functions return a promise. You would need to await or use .then syntax to get the value from the promise. It's not a promise the first time through because it returns initial state. Hope that helps!

jack.benson
  • 2,211
  • 2
  • 9
  • 17
  • Thanks, I tried that, instead of returning a promise it was returning undefined I don't know why. As @bsapaka mentioned, I moved the logic from inside the reducer to outside and passed the data as a payload. It worked, but now I can't access the reducer in other components, it always returns the initial state. I'm moving to Redux. I don't need anything complex, but I'm tired of wasting time with this. – bruxo00 Apr 17 '20 at 15:29
2

It returns a promise because the reducer is an async function, and this is because of the awaited call to AsyncStorage:

const value = JSON.parse(await AsyncStorage.getItem('user_info'));

You should move the storage fetching into an effect hook, and then dispatch the result of the resolved/rejected promise.

Another reason to move the data fetching out is that reducers should be pure functions (this is a link is to Redux docs, but the idea still applies to React reducers). The only source of data a reducer should have are its state and action parameters. This allows for deterministic behavior with respect respect to those parameters; given the same arguments, separate reducer calls should always return the same result.

brietsparks
  • 4,776
  • 8
  • 35
  • 69
  • Thank you, I did that, I moved the logic from inside the reducer to outside and passed the data as a payload. It worked, but now I can't access the reducer in other components, it always returns the initial state. I'm moving to Redux. I don't need anything complex, but I'm tired of wasting time with this. – bruxo00 Apr 17 '20 at 15:29
  • if you need that state in other components, move the reducer up the tree until all the components that need the data are under the component that has the reducer – brietsparks Apr 17 '20 at 17:06