使用钩子将基于redux类的组件重构为功能组件。收到有关异步操作的错误。为什么?

时间:2020-06-10 20:38:42

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

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

不幸的是,我陷入了一个问题,需要您的帮助。

该教程应用程序使用oauth登录gapi。为此,使用了一个按钮,根据所显示的授权状态,该按钮可以登录或注销。登录时,您可以在弹出窗口中选择一个Google帐户。 但是,当我单击此按钮登录时,出现错误,提示:

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

1)但是在我的应用程序中,我使用了名为useEffect()的钩子和.then,然后省略了thunk之类的中间件的使用。还是我错了?

2)我应该在应用中使用useEffect()useDispatch()还是可以通过useSelector()(或/和useDispatch()或/和{{1 }})

3)教程代码使用useEffect(),与{ connect }等效。我是否不使用不必要的useSelector()和/或useDispatch()?如果是,那我该如何在不调度的情况下更改按钮的文本?

这是教程代码:

useEffect()

我的代码:

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);

2 个答案:

答案 0 :(得分:1)

Redux有一些特定的用例和缺点,但是引入了一些缺点,例如很多样板。不知道为什么需要redux可能是您不需要redux的信号。

但是: useState和useEffect是两个基本的React原语。 [state, setState] = useState(initialState)使您可以使用状态并进行设置; 每当依赖项之一更改时,useEffect(callback, [deps])都会触发回调。 您可能不需要其他任何东西。

Redux使用不同的方法:

  1. 它对整个应用使用单个集中式状态(您可以使用useSelector()钩子进入任何组件
  2. 它允许您通过普通的javascript对象(例如{ type: 'SIGN_IN', payload: { token: '42' } })对其进行更改,并为每个对象定义状态应如何更改(定义了所谓的“ reducer”功能)。您可以通过dispatch()函数调度这些状态更改。

那效果如何? 由于调度仅将对象作为参数(因为在其他地方定义了业务逻辑),因此没有必要向其传递函数或承诺。 但是您可以通过几种方式触发它们:

  1. 直接在回调中,例如<button onClick={() => auth.signIn()}> Sign in </button>。很好。
  2. 在useEffect挂钩中,如果您需要作为状态更改的响应来触发效果。例如:useEffect( () => console.log(`count changed, new value is: ${count}`), [count])
  3. 通过redux中间件

选项1和2很好,但是缺点是您必须在本地定义业务逻辑,而不是将其与视图分离。 在其他位置定义它可以使您更轻松地扩展和维护应用程序。

自定义中间件增强了dispatch()的工作方式。 最简单的方法之一是redux-thunk。 它使您可以传递回调以进行分派:

// somewhere in the codebase:
function signIn(){
  window.gapi.auth2.getAuthInstance().signOut()
}

...

// Inside you button:
<button onClick={() => dispatch(signIn())}> Sign Out </button>

还有更复杂的效果模型,但是除非需要更强大的功能,否则您可以坚持使用redux-thunk。

请记住,每个框架只是一个具有特定权衡的工具,除非您真的有理由使用它们,否则您可以坚持使用出色的React原语,而不是过度设计自己的工具应用程序。

答案 1 :(得分:1)

您收到一条错误消息: 错误:动作必须是普通对象。使用自定义中间件执行异步操作。

是的。如果没有可以处理另一种操作的中间件,那将是行不通的。

1)但是,在我的应用程序中,我使用了带有.E的钩子useEffect(),然后省略了诸如thunk之类的中间件的使用。还是我错了?

useEffect.then与redux无关。你在这里错了。您正在使用的动作是thunk(返回function(dispatch,getState)的函数),并且您肯定需要redux-thunk

2)我应该在我的应用中使用useEffect()和useDispatch()还是可以通过useSelector()(或/和useDispatch()或/和useEffect())完成所有操作

安装组件时,您需要useEffect来调用第三方API。 您需要useDispatch才能获得dispatch函数。 您需要useSelector才能从redux-store获得值。

3)教程代码使用{connect},它等效于useSelector()。我是否不使用不必要的useDispatch()和/或useEffect()?如果是,那我该如何在不调度的情况下更改按钮的文本?

代码:(可以破解)

    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作为useEffect的依赖项。