调用后端后,动作函数总是在 React 中返回相同的 reducer 类型

Action function always returning same reducer type in React after calling backend

所以我正处于完成我的 2FA 方法的最后一步,现在我需要检测用户是否启用了他的方法,为了实现这一点,我创建了一个函数 return如下:

if (!user.twoFactorTokenEnabled) {
  // If user has not enabled twoFactorAuthentication, then
  // let him proceed and log in into his account
  sendTokenResponse(user, 200, req, res)
} else {
  // Otherwise, just return the following fields:
  // username, isEmailConfirmed and twoFactorTokenEnabled
  res.status(200).json({ success: true, data: user })
}

该功能位于后端,目前运行良好,至少我是这么认为的。 话虽如此,我每次提交表单时都会进入我的登录组件并触发 login 函数:

const [TFAEEnabled, setTFAEnabled] = useState(false)

login(loginData, history)
  .then((result) => {
    // If twoFactorTokenEnabled is found to be true
    // then call this function and set it to true and then
    // display and extra field to enter
    // a google authenticator code
    setTFAEnabled(result.payload.data.twoFactorTokenEnabled)
  })
  .catch((err) => {
    setTFAEnabled(false)
    setError(true)
  })

// This is found within my returned component.
{
  TFAEEnabled && (
    <Form.Control
      type="text"
      placeholder="Authenticator Code"
      aria-label="2FA-CODE"
      aria-describedby="2FA-CODE-text"
      autoComplete="2FA-CODE"
      name="2FA-CODE"
      id="2FA-CODE"
      minLength="6"
      required
      // onChange={handleChange('2FA-CODE')}
      value="2FA Code"
    />
  )
}

在上面的代码中,我试图展示我正在尝试完成的是“如果 X 为真,则只显示此组件”。然而,该函数根本无法正常工作,因为它总是以调用第一个 reducer 类型结束,这是登录函数:

export const login = (loginData, history) => async (dispatch) => {
  try {
    const res = await api.post(`/auth/login`, loginData)

    if (!res.data.twoFactorTokenEnabled) {
      // if twoFactorTokenEnabled === false then let
      // the user log in into his account normally
      // without requiring a token provided by Y
      // authenticator app
      dispatch({
        type: LOGIN_SUCCESS,
        payload: res.data,
      })
      // Dispatch the rest of the user data
      dispatch(loadUser())
      //  and let him in
      history.push('/timeline')
    } else {
      // otherwise, if twoFactorTokenEnabled === true
      // return the previously mentioned data given by
      // the backend: username, isEmailConfirmed and
      // twoFactorTokenEnabled
      return dispatch({
        type: LOGIN_SUCCESS_REQUEST,
        payload: res.data,
      })
    }
  } catch (err) {
    const error = err.response.data.message
    const errors = err?.response?.data?.errors

    if (error) {
      dispatch(setAlert(error, 'danger'))
    }

    if (errors) {
      errors.forEach((error) => dispatch(setAlert(error.msg, 'danger')))
    }
    dispatch({
      type: LOGIN_FAIL,
      payload: { msg: err.response?.statusText, status: err.response?.status },
    })
  }
}

后端工作得很好!它 return 是它应该 return 的数据,我认为问题出在我的 reducer 上:

// if twoFactorTokenEnabled === true
// call this reducer which should only return
// the 4 fields given by the backend
case LOGIN_SUCCESS_REQUEST:
  return {
    ...state,
    // ...payload,
    isAuthenticated: false,
    isTFAEnabled: payload,
    loading: false
  };
// Otherwise let the user in and return everything
case LOGIN_SUCCESS:
    return {
      ...state,
      ...payload,
      isAuthenticated: true,
      loading: false
    };

希望这足以解释,否则请随时索取更多信息。

谢谢!

我认为你正在使用 Redux-thunk,你需要调度你的动作函数。 然后可以从 Redux 商店中选择 TFAEEnabled。

const TFAEEnabled = useSelector(state => state.isAuthenticated);
const dispatch = useDispatch();

dispatch(login(loginData, history));

我实际上发现我不在 return 根据后端发送的数据,我不得不在 if 语句中 return 第二个 'data' :

const res = await api.post(`/auth/login`, loginData);

    if (res.data.data) {
      history.push(`/two-fa-validate/${res.data.data._id}`);
      return dispatch({
        type: LOGIN_SUCCESS_REQUEST,
        payload: res.data
      });
    }
    dispatch({
      type: LOGIN_SUCCESS,
      payload: res.data
    });
    dispatch(loadUser());
    history.push('/timeline');