3

I'm beginner with React/Redux. I want to authenticate a User and display a notification on my app when error occurs.

This is my login function:

  const [loading, setLoading] = useState(false);
  const dispatch = useDispatch();

  const handleSignIn = (values: SignInOpts, setSubmitting: any) => {
      setLoading(true);
      dispatch(authenticationActions.signInAndFetchUser(values))
        .then(res => {
          console.log("SignIn Success");
          setLoading(false);
        })
        .catch(err => {
          console.log("SignIn Failure");
          setLoading(false);
          showErrorNotification(
            "Error notification"
          );
        });
  };

My action:

export const signInAndFetchUser = (credentials: SignInOpts) => {
  return (dispatch, getState) => {
    return dispatch(signIn(credentials)).then(res => {
      const token = getState().authentication.token;
      return dispatch(getMe(token));
    });
  };
};

The error I have :

React/Redux error

How can I perform this ?

3 Answers 3

4

Generally for thunks, dispatch(myThunk()) will return whatever the thunk returns and can be a Promise, as also the case in your signInAndFetchUser method.

The problem is that the normal useDispatch hook typings do not know which middlewares you are using and thus have no overload for that behaviour.

That is why the Redux TypeScript Quickstart Tutorial recommends you define your own, correctly-typed hooks:

// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

If you are using the official Redux Toolkit (especially with TypeScript, you definitely should as it cuts out almost all type annotations), you can just get

export type AppDispatch = typeof store.dispatch

If you are using old-style vanilla Redux, just use ThunkDispatch as AppDispatch.

Sign up to request clarification or add additional context in comments.

1 Comment

I've tried your method and it's also working. Thanks.
1

Most of your work should happen in the thunk (the action). dispatch does not return a promise. So you have to handle your promise inside your thunk and dispatch the corresponding action, it will then be send to the reducer. The new state will reflects the changes.

Here is a thunk which should give you an idea of how it works :

export const signInAndFetchUser = (credentials: SignInOpts) => {
  return (dispatch, getState) => {
    dispatch(action.startLoading);
    signIn(credentials)
      .then((res) => {
        // do something with res : maybe recover token, and store it to the store ?
        // if the token is in the store already why do you need to signIn ?
        dispatch(action.signedInSuccess(res.token));

        getMe(res.token)
          .then((user) => {
            dispatch(action.getMeSucceded(user));
          })
          .catch((err) => dispatch(action.getMeFailed(err)));
      })
      .catch((err) => dispatch(action.signInFailed(err)));
  };
};

Or using async/await :

export const signInAndFetchUser = (credentials: SignInOpts) => {
  return async (dispatch, getState) => {
    dispatch(action.startLoading);

    try {
      const res = await signIn(credentials);
      dispatch(action.signedInSuccess(res.token));

      try {
        const user = await getMe(res.token);
        dispatch(action.getMeSucceded(user));
      } catch (err) {
        dispatch(action.getMeFailed(err));
      }
    } catch {
      dispatch(action.signInFailed(err));
    }
  };
};

1 Comment

This is wrong. In the case of a thunk, dispatch returns whatever the thunk returns, which in this case is a promise. The disptach function is just typed incorrectly in this case. I'll add an answer.
0

dispatch do not return promise so you cannot pipe .then. From Action the pipeline flows to reducer, and reducer returns the state back to your component via useSelector for functional component and connect for class based component. So you need to hook those to receive login success object via state

8 Comments

dispatch(myThunk()) will return whatever the thunk returns, so in this case it actually is a promise. It's just not in the typings - I've added an answer explaining it.
@phry true, dispatch returns whatever action you pass to it. But in between it handles it, so for async api call with thunk middleware the passed async function is being executed and state updating pending. So as dispatch just returns the action and in thunk case as the promise but we should not extract data by resolving that promise rather it should come via reducer state update that's what I want to say
While this is a perfectly valid approach to do things this strictly, it is also perfectly valid to just use that promise to do something in sequence in the component (or another thunk) - we even provide various examples on how you would do that if you want to in the Redux documentation. At that point, it's code style and per-application convention, but not a hard fact.
Technically it's possible to check the dispatch returned promise state and chain further in it, but that will be anti-pattern then. Because that will break the redux tenet of 'predictable state update'. It will create divergent path. We can handle further api call in action or once the state updated.
I am a Redux maintainer. This is totally okay. Data flow is predictable for a series of actions, not necessarily for the side-effects in-between, no matter in which way they are managed, if in a saga, within a thunk, or chained outside a thunk. There can always just be a server-side error or anything, side effects are per definition not predictable.
|

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.