代码之家  ›  专栏  ›  技术社区  ›  Dariusz Legizynski

将redux类组件重构为带有钩子的功能组件。获取有关异步操作的错误。为什么?

  •  0
  • Dariusz Legizynski  · 技术社区  · 5 年前

    我正在学习一个教程,老师正在使用 react-redux 上课。为了更好地理解它,我决定用钩子。

    不幸的是,我遇到了一个问题,需要你的帮助。

    教程应用程序使用oauth登录gapi。为此,使用了一个按钮,根据显示的授权状态,可以登录或注销。登录时,可以在弹出窗口中选择一个google帐户。 但当我点击这个按钮登录时,我得到一个错误,上面写着:

    错误:操作必须是普通对象。对异步操作使用自定义中间件。

    1) 但在我的应用程序中,我使用了一个叫做 useEffect() 然后省略中间件的使用,比如 thunk . 还是我错了?

    2) 我应该用吗 useDispatch() useSelector() (或/和 ,或/和 使用效果()

    3) 教程代码使用 { connect } . 我没有用不必要的吗 和/或 ? 如果是,那么我如何在不发送的情况下更改按钮的文本?

    以下是教程代码:

    import React from 'react';
    import { connect } from 'react-redux';
    import { signIn, signOut } from '../actions';
    
    class GoogleAuth extends React.Component {
      componentDidMount() {
        window.gapi.load('client:auth2', () => {
          window.gapi.client
            .init({
              clientId:
                '797401886567-9cumct9mrt3v2va409rasa7fa6fq02hh.apps.googleusercontent.com',
              scope: 'email'
            })
            .then(() => {
              this.auth = window.gapi.auth2.getAuthInstance();
    
              this.onAuthChange(this.auth.isSignedIn.get());
              this.auth.isSignedIn.listen(this.onAuthChange);
            });
        });
      }
    
      onAuthChange = isSignedIn => {
        if (isSignedIn) {
          this.props.signIn(this.auth.currentUser.get().getId());
        } else {
          this.props.signOut();
        }
      };
    
      onSignInClick = () => {
        this.auth.signIn();
      };
    
      onSignOutClick = () => {
        this.auth.signOut();
      };
    
      renderAuthButton() {
        if (this.props.isSignedIn === null) {
          return null;
        } else if (this.props.isSignedIn) {
          return (
            <button onClick={this.onSignOutClick} className="ui red google button">
              <i className="google icon" />
              Sign Out
            </button>
          );
        } else {
          return (
            <button onClick={this.onSignInClick} className="ui red google button">
              <i className="google icon" />
              Sign In with Google
            </button>
          );
        }
      }
    
      render() {
        return <div>{this.renderAuthButton()}</div>;
      }
    }
    
    const mapStateToProps = state => {
      return { isSignedIn: state.auth.isSignedIn };
    };
    
    export default connect(
      mapStateToProps,
      { signIn, signOut }
    )(GoogleAuth);
    

    我的密码是:

    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();
    
        useEffect(
            () => {
                const onAuthChange = () => {
                    if (isSignedIn) {
                        dispatch(signIn(window.gapi.auth2.getAuthInstance().currentUser.get().getId()));
                    } 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);
                        });
                });
            },
            [ dispatch, isSignedIn ]
        );
    
        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;
    
    0 回复  |  直到 5 年前
        1
  •  1
  •   Innokenty    5 年前

    出现错误消息: 错误:操作必须是普通对象。对异步操作使用自定义中间件。

    这是正确的。如果没有能够处理另一种类型操作的中间件,这将无法工作。

    1) 但在我的应用程序中,我使用了名为useffect()的钩子with。还是我错了?

    useEffect .then 与redux无关。你错了。您使用的操作是thunks(返回函数(dispatch,getState)的函数),您肯定需要一个 redux-thunk

    2) 我应该在应用程序中使用useffect()和useDispatch(),还是可以使用useSelector()完成所有操作(或/和useDispatch(),或/和useffect())

    在安装组件时调用第三方API。 useDispatch dispatch 你需要 useSelector 获取redux商店的值。

    3) 教程代码使用{connect},这相当于useSelector()。我没有使用不必要的useDispatch()和/或useffect()?如果是,那么我如何在不发送的情况下更改按钮的文本?

        const GoogleAuth = () => {
            const isSignedIn = useSelector((state) => state.auth.isSignedIn);
            const dispatch = useDispatch();
    
            useEffect(
                () => {
                    const onAuthChange = (isSignedInLocal) => {
                        if (isSignedInLocal) {
      dispatch(signIn(window.gapi.auth2.getAuthInstance().currentUser.get().getId()));
                        } 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());
                                
                                window.gapi.auth2.getAuthInstance().isSignedIn.listen(onAuthChange);
                            });
                    });
                },
                [dispatch]
            );
    
            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;

    确保你不提供 isSignedIn 作为对 .

        2
  •  1
  •   Alessandro Scandone    5 年前

    useState和useEffect是两个基本的React原语。 [state, setState] = useState(initialState) 允许您使用状态并设置它; useEffect(callback, [deps])

    Redux使用不同的方法:

    1. useSelector()
    2. 它允许您通过普通的javascript对象(如 { type: 'SIGN_IN', payload: { token: '42' } } dispatch()

    那效果怎么样? 由于dispatch只将对象作为参数(因为业务逻辑在别处定义),因此向其传递函数或承诺没有任何意义。 但你可以通过几种方式触发它们:

    1. <button onClick={() => auth.signIn()}> Sign in </button>
    2. 在useffect钩子中,如果需要触发状态更改的影响。例如。: useEffect( () => console.log(`count changed, new value is: ${count}`), [count])

    选项1和2非常好,但缺点是您必须在本地定义业务逻辑,而不是将其与视图分离。 在不同的地方定义它可以让你更容易地扩展和维护应用程序。

    调度() 最简单的方法之一是 redux-thunk .

    // somewhere in the codebase:
    function signIn(){
      window.gapi.auth2.getAuthInstance().signOut()
    }
    
    ...
    
    // Inside you button:
    <button onClick={() => dispatch(signIn())}> Sign Out </button>
    

    如果你有使用它们的理由,你可以坚持使用优秀的React原语,而不是毫无理由地过度设计你的应用程序。