Loading com Redux

Antes de tudo, o reducer deve possuir um estado loading que será definida como true no Request e false em caso de sucesso ou erro.

Arquivo src/store/modules/auth/reducer.js

import produce from 'immer';
const INITIAL_STATE = {
token: null,
signed: false,
loading: false,
};
export default function auth(state = INITIAL_STATE, action) {
return produce(state, draft => {
switch (action.type) {
case '@auth/SIGN_IN_REQUEST': {
draft.loading = true;
break;
}
case '@auth/SIGN_IN_SUCCESS': {
draft.token = action.payload.token;
draft.signed = true;
draft.loading = false;
break;
}
case '@auth/SIGN_FAILURE': {
draft.signed = true;
draft.loading = false;
break;
}
default:
}
});
}

O arquivo sagas deverá possuir um retorno em caso de erro, para isso usa-se o try/catch.

Arquivo src/store/modules/auth/sagas.js

import { takeLatest, call, put, all } from 'redux-saga/effects';
import { signInSuccess, signFailure } from './actions';
import api from '~/services/api';
import history from '~/services/history';
export function* signIn({ payload }) {
try {
const { email, password } = payload;
const response = yield call(api.post, 'sessions', {
email,
password,
});
const { token, user } = response.data;
if (!user.provider) {
console.tron.error('Usuário não é prestador');
}
yield put(signInSuccess(token, user));
history.push('/dashboard');
} catch (err) {
yield put(signFailure());
}
}
export default all([takeLatest('@auth/SIGN_IN_REQUEST', signIn)]);

No arquivo de SignIn deve se importar o useSelector e criar a variável loading:

Importação:

import { useDispatch, useSelector } from 'react-redux';

Criação da variável dentro do componente:

const loading = useSelector(state => state.auth.loading);

Implementação do efeito no botão:

<button
style={loading ? { cursor: 'wait' } : { cursor: 'pointer' }}
type="submit"
>
{loading ? 'Carregando...' : 'Acessar'}
</button>