HOC-功能组件

时间:2019-09-09 10:40:30

标签: javascript reactjs components higher-order-components

我已经在this之后在我的react应用程序中创建了一个HOC,它可以正常工作。但是我想知道是否有一种方法可以将HOC创建为功能组件(有状态或无状态)???因为给定的示例是基于类的组件。

试图在网上找到相同的东西,但什么也没得到。不知道那是否有可能?还是做正确的事?

任何潜在客户都会受到赞赏:)

4 个答案:

答案 0 :(得分:12)

我同意siraj,严格来说,accepted answer中的示例不是真正的HOC。 HOC的显着特征是它返回一个组件,而被接受的答案 中的PrivateRoute组件是 。因此,尽管它可以很好地完成预定的工作,但我认为这并不是HOC的一个很好的例子。

在功能组件世界中,最基本的HOC如下所示:

const withNothing = Component => ({ ...props }) => (
  <Component {...props} />
);

调用withNothing返回另一个组件(不是实例,这是主要区别),然后可以像常规组件一样使用它:

const ComponentWithNothing = withNothing(Component);
const instance = <ComponentWithNothing someProp="test" />;

使用此方法的一种方法是,如果您想使用临时(没有双关语意的 lol )上下文提供程序。

比方说,我的应用程序有多个用户可以登录的地方。我不想在所有这些点上复制登录逻辑(API调用和成功/错误消息),所以我想要一个可重用的<Login />组件。但是,在我的情况下,所有这些登录点在视觉上都显着不同,因此不能选择可重用的组件。我需要一个可重用的<WithLogin />组件,该组件将为其子级提供所有必需的功能-API调用和成功/错误消息。这是执行此操作的一种方法:

// This context will only hold the `login` method.
// Calling this method will invoke all the required logic.
const LoginContext = React.createContext();
LoginContext.displayName = "Login";

// This "HOC" (not a true HOC yet) should take care of
// all the reusable logic - API calls and messages.
// This will allow me to pass different layouts as children.
const WithLogin = ({ children }) => {
  const [popup, setPopup] = useState(null);

  const doLogin = useCallback(
    (email, password) =>
      callLoginAPI(email, password).then(
        () => {
          setPopup({
            message: "Success"
          });
        },
        () => {
          setPopup({
            error: true,
            message: "Failure"
          });
        }
      ),
    [setPopup]
  );

  return (
    <LoginContext.Provider value={doLogin}>
      {children}

      {popup ? (
        <Modal
          error={popup.error}
          message={popup.message}
          onClose={() => setPopup(null)}
        />
      ) : null}
    </LoginContext.Provider>
  );
};

// This is my main component. It is very neat and simple
// because all the technical bits are inside WithLogin.
const MyComponent = () => {
  const login = useContext(LoginContext);

  const doLogin = useCallback(() => {
    login("a@b.c", "password");
  }, [login]);

  return (
    <WithLogin>
      <button type="button" onClick={doLogin}>
        Login!
      </button>
    </WithLogin>
  );
};

不幸的是,这不起作用,因为LoginContext.Provider是在内部 MyComponent实例化的,因此useContext(LoginContext)不返回任何内容。

HOC来救援!如果我加了一个小中间人怎么办?

const withLogin = Component => ({ ...props }) => (
  <WithLogin>
    <Component {...props} />
  </WithLogin>
);

然后:

const MyComponent = () => {
  const login = useContext(LoginContext);

  const doLogin = useCallback(() => {
    login("a@b.c", "password");
  }, [login]);

  return (
    <button type="button" onClick={doLogin}>
      Login!
    </button>
  );
};

const MyComponentWithLogin = withLogin(MyComponent);

Bam! MyComponentWithLogin现在可以正常工作了。

这可能不是解决这种特殊情况的最佳方法,但我有点喜欢。

是的,它确实只是一个额外的函数调用,仅此而已!根据官方指南:

HOC本身不是React API的一部分。它们是从React的组成性质中出现的一种模式。

答案 1 :(得分:4)

以下是将 HOC 与功能组件一起使用的过度简化示例。

要“包装”的功能组件:

import React from 'react'
import withClasses from '../withClasses'

const ToBeWrappedByHOC = () => {
return (
    <div>
        <p>I'm wrapped by a higher order component</p>
    </div>
       )
}

export default withClasses(ToBeWrappedByHOC, "myClassName");

高阶组件:

import React from 'react'


const withClasses = (WrappedComponent, classes) => {
return (props) => (
    <div className={classes}>
        <WrappedComponent {...props} />
    </div>
       );
};

export default withClasses;

该组件可以像这样在不同的组件中使用。

<ToBeWrappedByHOC/>

答案 2 :(得分:1)

例如,您绝对可以创建一个功能性的无状态组件,该组件接受该组件作为输入,并返回其他一些组件作为输出;

  1. 您可以创建一个PrivateRoute组件,该组件接受一个组件作为prop值,并根据用户是否通过身份验证返回其他一些组件。
  2. 如果用户未通过身份验证(从上下文存储中读取),则使用<Redirect to='/login'/>将用户重定向到登录页面,否则返回作为道具传递的组件,并将其他道具发送到该组件<Component {...props} /> < / li>

App.js

const App = () => {
  return (
      <Switch>
        <PrivateRoute exact path='/' component={Home} />
        <Route exact path='/about' component={About} />
        <Route exact path='/login' component={Login} />
        <Route exact path='/register' component={Register} />
      </Switch>
  );
}

export default App;

PrivateRoute.jsx

import React, { useContext , useEffect} from 'react';
import { Route, Redirect } from 'react-router-dom'
import AuthContext from '../../context/auth/authContext'

const PrivateRoute = ({ component: Component, ...rest }) => {
  const authContext = useContext(AuthContext)
  const { loadUser, isAuthenticated } = authContext
  useEffect(() => {
    loadUser()
    // eslint-disable-next-line
  }, [])
  if(isAuthenticated === null){
    return <></>
  }
  return (
    <Route {...rest} render={props =>
      !isAuthenticated ? (
        <Redirect to='/login'/>
      ) : (
        <Component {...props} />
      )
    }
    />
  );
};
export default PrivateRoute;

高阶组件不必是类组件,它们的目的是根据某种逻辑将一个组件作为输入并返回一个组件作为输出。

答案 3 :(得分:0)

当然,您可以在react中创建功能性HOC,也可以为此创建任何其他文件夹,例如“ Utils”。例如,这是我在Utils文件夹中的amountUtil.js文件:

export const getFormattedAmount =(金额?:金额)=>(   金额&& ${amount.formattedAmount} ${amount.currency} );