【问题标题】:Why, while using useEffect() and .then() in Redux, I get an Error: Actions must be plain objects. Use custom middleware for async actions为什么,在 Redux 中使用 useEffect() 和 .then() 时,我得到一个错误:Actions must be plain objects。使用自定义中间件进行异步操作
【发布时间】:2020-06-10 18:04:02
【问题描述】:

使用 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;

reducer/index.js

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

export default combineReducers({
    auth: authReducer
});

reducers/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;
    }
};

actions/index.js

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

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

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

types.js

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

【问题讨论】:

    标签: redux oauth oauth-2.0 react-redux react-hooks


    【解决方案1】:

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

    第二个错误的原因是 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])
    

    【讨论】:

    • 感谢您的回答。它确实对 useEffect 中的 onAuthChange 有所帮助。我只是想知道,为什么我需要一个像 thunk 这样的中间件来让应用程序正常工作,或者在这种情况下可以省略 useEffect 吗?
    • 您可以使用 React 原语(钩子)来处理效果和状态管理,或者使用不同的框架,如 redux。除非你真的有使用 redux 的理由,否则我建议你一开始就不要使用它;您可以只使用: [isSignedIn, setIsSignedIn] = useState(false) 如果您想将效果与范围内的某个变量同步,您只需要 useEffect,但在回调中执行它是完全可以的,例如:
    猜你喜欢
    • 1970-01-01
    • 2020-07-24
    • 2021-03-07
    • 2023-02-02
    • 2016-12-20
    • 2017-05-01
    • 1970-01-01
    • 2022-11-21
    相关资源
    最近更新 更多