React HOC 组件的定义风格

Style of defining React HOC components

因此,在我的 React 应用程序中,我看到了以下用于定义 HOC 的模式;

export function withMyApi() {
  // Extra function wrapper to allow for clear place for configs.  Plus, you can compose multiple HOCs if necessary
  return function(WrappedComponent) {
    class MyApiUrls extends React.Component {
      constructor(props) {
        super(props);
      }
      render() {
        return <WrappedComponent api={this.api} {...this.props} />;
      }
    }
    return MyApiUrls;
  };
}

以上是如何工作的? HIC这里是不是返回了一个函数组件? 上面的方式是不是故意用来编的?

具体来说,HOC returns 要么是 class 组件,要么是函数组件。但是,对于上述模式,HOC 返回的内容并不清楚。

您问题中的函数是名称为 withMyApi 的 HOF(高阶函数),其中 returns 是接受组件的 匿名 函数作为 WrappedComponent 参数,当被调用时,returns 您传递的“组件”。

这是使用此 HOF 的语法:

const MyComponent = withMyApi()(MyOriginalComponent)

是的,你可以说withMyApi()(MyOriginalComponent)不必要的工作,应该只是withMyApi(MyOriginalComponent).

我同意。因此,这里是重构的 HOC:

选项 1,使用 class 组件作为包装器:

export function withMyApiClass(WrappedComponent) {
  return class MyApiUrls extends React.Component {
    constructor(props) {
      super(props)
      this.api = 'http://www.example.com'
    }
    render() {
      return <WrappedComponent api={this.api} {...this.props} />
    }
  }
}

使用上述 HOC 的语法:const Comp1 = withMyApiClass(Comp).

选项 2,使用函数组件作为包装器:

export function withMyApiFunction(WrappedComponent) {
  return function MyApiUrls(props) {
    const api = useRef('http://www.example.com')
    return <WrappedComponent api={api.current} {...props} />
  }
}

使用上述 HOC 的语法:const Comp1 = withMyApiFunction(Comp).


那么,为什么我们需要 withMyApi HOF 的第一种形式(有问题)?

您可能需要也可能不需要,具体取决于您的要求。一个典型的用例是传递一些 config 对象。一个例子:

export function withMyApi(config) {
  const { secure } = config
  return function (WrappedComponent) {
    class MyApiUrls extends React.Component {
      constructor(props) {
        super(props)
        this.api = `http${secure ? 's' : ''}://www.example.com`
      }
      render() {
        return <WrappedComponent api={this.api} {...this.props} />
      }
    }
    return MyApiUrls
  }
}

使用上述 HOF 的语法:const Comp1 = withMyApi({ secure: false })(Comp).

PS: 如果你曾经使用过 Redux,你一定见过 connect HOF,它接受一些参数,类似于你的 HOF。

因此编写 HOF 的基本思想是 - 将配置参数与组件参数分开,以便人们在有多个 HOC 时可以轻松使用 compose 功能 .


您的问题:

以上是如何工作的?

有问题的 HOC 实际上是 高阶函数:它 returns 一个函数,当被调用时,returns 一个组件。

这里HOF返回的是函数组件吗?

不是,是返回一个函数。但是当我们调用returned函数时,它returns一个class组件。见选项2,即returns一个函数组件。

上面的方式是不是特意用来编的?

是的,您可以使用 compose 实用程序(来自任何库,例如 - Ramdajs, Lodash, or Redux) to create a new "composed HOC" when using multiple HOCs or HOFs. See Maximizing Composability.


编辑:

我不喜欢 HOF。还能实现组合配置吗?

是的,一个例子。假设我们在下面写了 HOC:

export function withMyApi(WrappedComponent, config) {
  const { secure } = config
  return function MyApiUrls(props) {
    const api = useRef(`http${secure ? 's' : ''}://www.example.com`)
    return <WrappedComponent api={api.current} {...props} />
  }
}

如何使用?

const Comp1 = withMyApi(Comp, { secure: true })

/* OR, if you want to use more HOCs.
Nested (dirty? Maybe) */
const Comp1 = withRouter(withMyApi(Comp, { secure: true })) 

-----------------------------------------------------------

// But you CAN NOT compose like this:
const composedHOC = compose(
  withRouter,  // a function : OK
  withMyApi(Comp, { secure: false }) // NOT OK. Because is a React Component now.
)
/* It will give error when you will call composedHOC(...) because 
in React, we don't call components using parenthesis i.e. () but 
we invoke then using angle brackets i.e. < />. */

/* After compsing, you CAN NOT do the below otherwise you are going 
to pass `Comp` 2 times to `withMyApi` : Bad/Confusing... :( 
PLUS you will see errors due to calling a React Component using
parenthesis i.e. () as mentioned above. */
const Comp1 = composedHOC(Comp)

-----------------------------------------------------------

// But you can compose like the below:
const composedHOC = compose(
  withRouter, 
  withMyApi
)
const Comp1 = composedHOC(Comp, { secure: false })
/* It will work because it will pass the TWO parameters - "Comp" & 
"{ secure: false }" to LAST (withMyApi) HOC. And the result of that 
will be passed to FIRST HOC (withRouter). */

-----------------------------------------------------------

// But, surprisingly, there will be error when you change the order:
const composedHOC = compose(
  withMyApi, // FIRST
  withRouter // LAST
)
const Comp1 = composedHOC(Comp, { secure: false })
/* Because it will pass the TWO parameters to LAST HOC (withRouter), 
and result of that to FIRST HOC (withMyApi) and So, withMyApi will 
receive ONLY enhanced "Comp" NOT the "{ secure: false }". This is why
using "compose" is buggy for HOCs that acpect config parameters. */

/* Just to remind, this is what a compose function does:
"Composes single-argument functions from right to left. The rightmost 
function can take multiple arguments as it provides the signature for 
the resulting composite function."
compose(f, g, h) is the same as (...args) => f(g(h(...args)))
Means, it pass args to h, and the result to g and then result to f. */

好吧,我可能喜欢HOF。如何使用 HOF 做同样的事情?

export function withMyApi(config) {
  const { secure } = config
  return function (WrappedComponent) {
    return function MyApiUrls(props) {
      const api = useRef(`http${secure ? 's' : ''}://www.example.com`)
      return <WrappedComponent api={api.current} {...props} />
    }
  }
}

如何使用?

const Comp1 = withMyApi({ secure: false })(Comp)

// OR, if you want to use more HOCs
const composedHOC = compose(
  withRouter,
  withMyApi({ secure: false })
)
const Comp1 = composedHOC(Comp) // Looks clean :)

结论:

  1. 如果 HOC 接受“组件”和一个或多个“配置”对象,请不要将 compose 与它们一起使用。请改用嵌套方法。
  2. 对 HOF 和 HOC 使用嵌套或更好的方式 - compose(只要 HOC 不接受配置对象)
  3. 当您想接受 one/more config 对象时,最好 编写 HOF。这样,人们就可以将 compose 与您的 HOF 一起使用。

如果您想通过函数的附加参数来支持“配置”,HOF 会更好/更清晰。

This (HOF) form may seem confusing or unnecessary, but it has a useful property. Single-argument HOCs like the one returned by the connect function have the signature Component => Component. Functions whose output type is the same as its input type are really easy to compose together.

它实际上 return 是您将用于 return 一个 Class 组件的函数。

我认为混淆在于 HOC 的概念,即具有包装函数的上述模式。

这种模式允许我们在不破坏 HOC 概念的情况下增强我们的组件。

我们需要保持 HOC 的概念,但我们可能需要添加额外的配置或组合多个组件,以上模式将很有用。

Component => Component

以上变为:

WrapperFunction()(Component) => Component

但我们想知道为什么不直接通过 HOC 传递另一个参数:

HOC(Component, <any extra config we want>)

HOC 说,它需要一个组件和 return 一个组件和额外的参数似乎不是很积极,不是吗?