GithubHelp home page GithubHelp logo

Comments (5)

BlackGanglion avatar BlackGanglion commented on April 29, 2024 17

一上来就阐述理论总显得太过于枯燥,我想结合精读文章,通过 Form 组件抽象来说明 HOC 所具有的良好扩展机制。

Form 是怎样的

  • Form 中会包含各种不同的组件,常见的有 Input、Selector、Checkbox 等等,也会有根据业务需求加入的自定义组件。
  • Form 灵活多变,从功能上看,表单校验可能为单组件值校验,也可能为全表单值校验,可能为常规检验,比如:非空、输入限制,也可能需要与服务端配合,甚至需要根据业务特点进行定制。从 UI 上看,检验结果显示的位置,可能在组件下方,也可能是在组件右侧。

Form 为何要使用 HOC

直接裸写 Form,无疑是机械而又重复的。将 Form 中组件的 value 经过 validator,把 value,validator 产生的 error 信息储存到 state 或 redux store 中,然后在 view 层完成显示。这条路大家都是相同的,可以进行复用,只是我们面对的是不同的组件,不同的 validator,不同的 view 而已。

对于 Form 而言,既要满足通用,又要满足部分个性化的需求,以往单纯的配置化只会让使用愈加繁琐,我们所需要抽象的是 Form 功能而非 UI,因此通过 HOC 针对 Form 的功能进行提取就成为了必然。

如何实现 Form 的 HOC

image

首先将表单中的组件(Input、Selector...)与相应 validator 与组件值回调函数名(trigger)传入 Decorator,将 validator 与 trigger 相绑定。Decorator 完成了各种不同组件与 From 内置 Store 间 value 的传递、校验功能的抽象,即精读文章中提到 Props Proxy 方式的其中两种作用:提取state操作props

function formFactoryFactory({
  validator,
  trigger = 'onChange',
  ...
}) {
  return FromFactory(WrappedComponent) {
    return class Decorator extends React.Component {
      getBind(trigger, validator) {
        ...
      }
      render() {
        const newProps = {
          ...this.props,
          [trigger]: this.getBind(trigger, validator),
          ...
        }
        return <WrappedComponent {...newProps} />
      }
    }
  }
}

// 调用
formFactoryFactory({
  validator: (value) => {
    return value !== '';
  }
})(<Input placeholder="请输入..." />)

当然为了考虑个性化需求,Form Store 也向外暴露很多 API,可以直接获取和修改 value、error 的值。现在我们需要对一个表单的所有值提交到后端进行校验,根据后端返回,分别列出各项的校验错误信息,就需要借助相应项的 setError 去完成了。

这里主要参考了 rc-form 的实现方式,有兴趣的同学可以阅读其源码。

import { createForm } from 'rc-form';

class Form extends React.Component {
  submit = () => {
    this.props.form.validateFields((error, value) => {
      console.log(error, value);
    });
  }

  render() {
    const { getFieldError, getFieldDecorator } = this.props.form;
    const errors = getFieldError('required');
    return (
      <div>
        {getFieldDecorator('required', {
          rules: [{ required: true }],
        })(<Input />)}
        {errors ? errors.join(',') : null}
        <button onClick={this.submit}>submit</button>
      </div>
    );
  }
}

export createForm()(Form);

from weekly.

monkingxue avatar monkingxue commented on April 29, 2024 7

从 type system 的角度来看看 HOC

我想换个角度来谈谈我对 HOC 的浅见。

在我看来,React 中产生 HOC 简直是天经地义的事情,原因其实很简单,在 React 中组件是 first class 的,这样我们就可以利用 type system 的眼光来看待 component。

根据业务来划分,我们一种组件就是一种 type,借用 haskell 中的一个概念,这个组件的 kind 是 *,那么很明显,HOC 的 kind 就是 *->*。众所周知,kind 是对于 type 的抽象,React 中之所以出现这种抽象,很明显是业务的需要,换句话来说,我们的业务是基于 type system 构建的,我觉得这对于前端是一个巨大的进步。为什么呢?因为借助 type system,我们可以在编译期就静态避免很多 bug。原来的 JS,是 compile OK,but doesn't work,其中一个原因是因为 JS 的弱类型,这很好解决,用 TS 能大大缓解这个问题。而另外一个的原因就是业务的无类型。

为什么说业务无类型呢?回想开发 cocos、.NET这些东西的日子,由于语言的 OO 特性,我不得不把业务操作封装在一个个 class 中。过度封装当然不好,但是由于 class 即 type 的特性,我甚至在 extend 一个 class 的时候,就自动完成了 inherient 和 subtype 。当我需要修改某一个业务的 class 的时候,我就不得不去考虑 type 的关系,否则 IDE 直接静态给我一个 error。反观 JS,无论我对一个业务的封装做怎样的修改,IDE 都没什么反应,然后我就只能对着没有反应的页面发呆,再默默地打开 dev tool 一步步断点。而引入 HOC,其实就是加强了业务上的类型关联度,而这种关联度,就是静态检测的保证。

当然,前端的多副作用让严格的 type system 基本没有什么用武之地,想想也是,如何在数学中定义一系列副作用的推导关系呢?但是这并不是说 HOC 没什么用处,别忘了那个著名的公式 view = render(state),在一个可以自洽的组件系统中,HOC 是一种很棒的抽象,比如 @BlackGanglion 聚聚文章中所举的 select 和 search 的例子。

唔,脑洞可以大一点,比如我们完全可以利用 HOC 来实现 React 的 implement 啊,把操作和 view 分离,让一种业务类型(比如 input)的 dumb component 都 extends input action 的 HOC,既是漂亮的抽象,也有静态类型检查的优点。

当然,最后的最后,不可过度抽象 仍是最重要的一点,就我的实践而言,HOC 的抽象成本还是有点高的,在大型项目中,合理使用 HOC 十分有必要,而小型项目嘛,老夫(大雾)写代码就一梭子~

from weekly.

camsong avatar camsong commented on April 29, 2024 4

原文中也提到了 HOC 和 Parent Component 的区别。对比来看:

React 的范式是:

view = render(state)

HOC 的范式是:

compose(render)(state)

parent component 的范式是:

render(render(state))

因此 @monkingxue 说完全可以利用 HOC 来实现 React 的 implement 啊,把操作和 view 分离,但这样做虽然功能上没问题,但一点也不优雅!谨防过度设计。

View 中逻辑从 DOM 相关性上划分为三种:

  1. DOM 相关。建议使用 parent component
  2. DOM 不相关,如校验、权限、请求发送、数据转换这类,通过数据变化间接控制 DOM。可以使用 HOC
  3. 交叉的部分,DOM 相关,但可以做到完全内聚,即这些 DOM 不会和外部有关联。均可

DOM 的渲染适合使用 parent component,这是 React JSX 原生支持的方式,清晰易懂。最好是能封装成 dumb component 中。
HOC 适合做 DOM 不相关又是多个组件共性的操作。如 Form 中,validator 校验操作就是纯数据操作的,放到了 HOC 中。但展示 Error 信息没有放到 HOC 中。但如果能把 Error 信息展示这些逻辑能够完全隔离,也可以放到 HOC 中。

“请求发送”是另一类 DOM 不相关的场景,react-refetch 的实现就是使用了 HOC,做到了高效和优雅:

connect(props => ({
  usersFetch: `/users?status=${props.status}&page=${props.page}`,
  userStatsFetch: { url: `/users/stats`, force: true }
}))(UsersList)

from weekly.

alcat2008 avatar alcat2008 commented on April 29, 2024

提到 HOC,首先想到的就是这篇文章 基于Decorator的组件扩展实践,就是 @BlackGanglion 的大作。

Compose + Decorator 的方式确实相当优雅,应用场景包括并不限于组件、函数。

这种模式下,每个 C 都是薄薄的一层,单一职责,维护起来极其方便。

目前我们团队就采用这种模式,省时省力省心。

from weekly.

jasonslyvia avatar jasonslyvia commented on April 29, 2024

希望大家能在这里分享观点,而不是简单的分享资料。

from weekly.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.