首页 > 解决方案 > 为什么,在 Redux 中使用 useEffect() 和 .then() 时,我得到一个错误:Actions must be plain objects。使用自定义中间件进行异步操作

问题描述

使用 Redux,现在我在使用 oauth 时遇到了登录和注销按钮。

当我按下按钮登录时,会出现弹出窗口,我可以选择一个帐户。但与此同时,网页会引发错误。

在此处输入图像描述

如标题中所述,我收到以下错误:错误:操作必须是普通对象。使用自定义中间件进行异步操作。我正在使用钩子,在这种情况下使用 useEffect().then() 来获取数据。

在此处输入图像描述

1)为什么?

2)也不知道,为什么我会收到警告:'onAuthChange' 函数使 useEffect Hook(第 35 行)的依赖关系在每次渲染时都发生变化。将它移到 useEffect 回调中。或者,将“onAuthChange”定义包装到它自己的 useCallback() Hook react-hooks/exhaustive-deps

这是我的代码:

GoogleAuth.js

import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { signIn, signOut } from "../actions";

const API_KEY = process.env.REACT_APP_API_KEY;

const GoogleAuth = () => {
    const isSignedIn = useSelector((state) => state.auth.isSignedIn);
    console.log("IsSignedIn useSelector: " + isSignedIn);
    const dispatch = useDispatch();

    const onAuthChange = () => {
        if (isSignedIn) {
            dispatch(signIn());
        } else {
            dispatch(signOut());
        }
    };

    useEffect(
        () => {
            window.gapi.load("client:auth2", () => {
                window.gapi.client
                    .init({
                        clientId: API_KEY,
                        scope: "email"
                    })
                    .then(() => {
                        onAuthChange(window.gapi.auth2.getAuthInstance().isSignedIn.get());
                        console.log("isSignedIn.get(): " + window.gapi.auth2.getAuthInstance().isSignedIn.get());
                        window.gapi.auth2.getAuthInstance().isSignedIn.listen(onAuthChange);
                    });
            });
        },
        [ onAuthChange ]
    );

    const onSignInOnClick = () => {
        dispatch(window.gapi.auth2.getAuthInstance().signIn());
    };

    const onSignOutOnClick = () => {
        dispatch(window.gapi.auth2.getAuthInstance().signOut());
    };

    const renderAuthButton = () => {
        if (isSignedIn === null) {
            return null;
        } else if (isSignedIn) {
            return (
                <button onClick={onSignOutOnClick} className="ui red google button">
                    <i className="google icon" />
                    Sign Out
                </button>
            );
        } else {
            return (
                <button onClick={onSignInOnClick} className="ui red google button">
                    <i className="google icon" />
                    Sign In with Google
                </button>
            );
        }
    };

    return <div>{renderAuthButton()}</div>;
};

export default GoogleAuth;

减速器/index.js

import { combineReducers } from "redux";
import authReducer from "./authReducer";

export default combineReducers({
    auth: authReducer
});

减速器/authReducer.js

import { SIGN_IN, SIGN_OUT } from "../actions/types";

const INITIAL_STATE = {
    isSignedIn: null
};

export default (state = INITIAL_STATE, action) => {
    switch (action.type) {
        case SIGN_IN:
            return { ...state, isSignedIn: true };
        case SIGN_OUT:
            return { ...state, isSignedIn: false };
        default:
            return state;
    }
};

动作/index.js

import { SIGN_IN, SIGN_OUT } from "./types";

export const signIn = () => {
    return {
        type: SIGN_IN
    };
};

export const signOut = () => {
    return {
        type: SIGN_OUT
    };
};

类型.js

export const SIGN_IN = "SIGN_IN";
export const SIGN_OUT = "SIGN_OUT";

标签: reduxoauthoauth-2.0react-reduxreact-hooks

解决方案


第一个错误的原因是,在onSignInOnClickand内部onSignInOnClick,都dispatch()接收到 a Promise(因为window.gapi.auth2.getAuthInstance().signIn()返回 a Promise)。在 redux 中处理效果有不同的解决方案,最简单的是redux promiseredux thunk。否则,您可以调度该{ type: SIGN_IN }操作,并编写一个自定义中间件来处理它。

第二个错误的原因是 onAuthChange 在每次渲染时都被重新定义,你可以在这里看到:

const f = () => () => 42
f() === f() // output: false

这是修复警告的可能解决方案:

useEffect(() => {
  const onAuthChange = () => {
    if (isSignedIn) {
      dispatch(signIn())
    } else {
      dispatch(signOut())
    }
  }

  window.gapi.load('client:auth2', () => {
    window.gapi.client
      .init({
        clientId: API_KEY,
        scope: 'email',
      })
      .then(() => {
        onAuthChange(window.gapi.auth2.getAuthInstance().isSignedIn.get())
        console.log(
          'isSignedIn.get(): ' +
            window.gapi.auth2.getAuthInstance().isSignedIn.get(),
        )
        window.gapi.auth2.getAuthInstance().isSignedIn.listen(onAuthChange)
      })
  })
}, [isSignedIn])

推荐阅读