我将如何使用React Hooks替换withAuth()HOC?

时间:2019-07-09 01:58:44

标签: reactjs redux react-hooks

我已经花了很多时间来阅读React Hooks,尽管与使用带有局部状态和生命周期方法的类相比,该功能看起来更加直观,易读和简洁,但我一直在阅读关于Hooks的替代文献。 HOC。

我在React应用程序中使用的主要HOC是withAuth-基本上是一个函数,用于检查currentUser(以Redux状态存储)是否已通过身份验证,如果是,则呈现包装的组件。

这是此的实现:

import React, { Component } from "react";
import { connect } from "react-redux";

export default function withAuth(ComponentToBeRendered) {
  class Authenticate extends Component {
    componentWillMount() {
      if (this.props.isAuthenticated === false) {
        this.props.history.push("/signin");
      }
    }
    componentWillUpdate(nextProps) {
      if (nextProps.isAuthenticated === false) {
        this.props.history.push("/signin");
      }
    }
    render() {
      return <ComponentToBeRendered {...this.props} />;
    }
  }

  function mapStateToProps(state) {
    return { isAuthenticated: state.currentUser.isAuthenticated };
  }

  return connect(mapStateToProps)(Authenticate);
}

我看不到如何用钩子替换此HO​​C,尤其是因为在调用render方法之后,钩子才运行。这意味着我将无法对以前被保护的组件(用withAuth包裹)使用钩子来确定是否渲染它,因为它已经被渲染了。

处理这种情况的新奇的花哨方式是什么?

2 个答案:

答案 0 :(得分:2)

render()

我们可以重新构造“渲染还是不渲染”的问题。在基于挂钩的回调或生命周期方法之前,将始终调用render方法。除了即将推出的deprecated lifecycle methods以外,其他所有内容都适用。

因此,您的render方法(或功能组件)必须处理其所有可能的状态,包括不需要渲染的状态。要么,要么什么都不做的工作可以被提升到父组件。两者之间的区别是

const Child = (props) => props.yes && <div>Hi</div>;
// ...
<Parent>
  <Child yes={props.childYes} />
</Parent>

const Child = (props) => <div>Hi</div>;
// ...
<Parent>
  {props.childYes && <Child />}
</Parent>

根据情况决定使用哪种。

挂钩

有多种方法可以使用钩子来解决HOC所做的相同问题。我将从HOC提供的内容开始;一种访问有关应用程序状态的用户数据的方法,并在数据表示无效会话时重定向到/signin。我们可以为这两个东西提供钩子。

import { useSelector } from "react-redux";

const mapState = state => ({
  isAuthenticated: state.currentUser.isAuthenticated
});

const MySecurePage = props => {
  const { isAuthenticated } = useSelector(mapState);

  useEffect(
    () => {
      if (!isAuthenticated) {
        history.push("/signin");
      }
    },
    [isAuthenticated]
  );
  return isAuthenticated && <MyPage {...props} />;
};

以上示例中发生了几件事。就像我们以前使用useSelector一样,我们正在使用react-redux中的connect钩子来访问状态,只是代码更少。

我们还使用从useSelector获得的值来通过useEffect挂钩有条件地触发副作用。默认情况下,每次渲染后都会调用我们传递给useEffect的回调。但是在这里,我们还传递了一个依赖项数组,它告诉React我们只希望当依赖项发生更改时才触发效果(除了第一个渲染器(始终会触发效果))。因此,当isAuthenticated开始为假或变为假时,我们将被重定向。

尽管此示例使用了组件定义,但它也可以用作自定义挂钩:

const mapState = state => ({
  isAuthenticated: state.currentUser.isAuthenticated
});

const useAuth = () => {
  const { isAuthenticated } = useSelector(mapState);

  useEffect(
    () => {
      if (!isAuthenticated) {
        history.push("/signin");
      }
    },
    [isAuthenticated]
  );
  return isAuthenticated;
};

const MySecurePage = (props) => {
  return useAuth() && <MyPage {...props} />;
};

最后一件事-您可能会想做这样的事情:

const AuthWrapper = (props) => useAuth() && props.children;

为了能够做这样的事情:

<AuthWrapper>
  <Sensitive />
  <View />
  <Elements />
</AuthWrapper>

您可能会认为最后一个示例是适合您的方法,但是我会在决定之前read this

答案 1 :(得分:-1)

backtick 提供的答案为基础,这段代码应该可以满足您的需求:

import React, { useEffect } from "react";
import { useSelector } from "react-redux";

const withAuth = (ComponentToBeRendered) => {
    const mapState = (state) => ({
        isAuthenticated: state.currentUser.isAuthenticated,
    });

    const Authenticate = (props) => {
        const { isAuthenticated } = useSelector(mapState);

    useEffect(() => {
        if (!isAuthenticated) {
            props.history.push("/signin");
        }
    }, [isAuthenticated]);
        return isAuthenticated && <ComponentToBeRendered {...props} />;
    };
    return Authenticate;
};

export default withAuth;

您可以使用React-Router-DOM将其呈现在容器中,如下所示:

import withAuth from "../hocs/withAuth"
import Component from "../components/Component"

// ...

<Route path='...' component={withAuth(Component)} />