React Higher Order Components - 构建可重用的业务逻辑

Higher Order Components(HOC) 的本质是Higher Order Functions,只不过返回值不是普通函数,而是React的component。利用HOC我们可以把相似的业务逻辑抽离出来,然后组件只负责渲染视图和组件相关的业务逻辑,而公用的业务逻辑则使用HOC中定义好的公共逻辑,以实现代码复用。本文将用两个简单的Form组件来演示HOC的基本使用方法。

运行效果

CodeSandBox:

创建Form组件

首先我们来创建一个未使用HOC的Form组件:

import React from "react";

class PersonalInfoForm extends React.Component {
  state = {
    name: "",
    age: 0
  };
  handleInputChange = event => {
    const target = event.target;
    const value = target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  };
  handleSubmit = event => {
    event.preventDefault();
    console.log(this.state);
  };
  render() {
    return (
      <form>
        <div>
          <label>
            Name:
            <input
              type="text"
              value={this.state.name}
              name="name"
              onChange={this.handleInputChange}
            />
          </label>
        </div>
        <div>
          <label>
            Age:
            <input
              type="number"
              value={this.state.age}
              name="age"
              onChange={this.handleInputChange}
            />
          </label>
        </div>
        <div>
          <button onClick={this.handleSubmit}>Submit</button>
        </div>
      </form>
    );
  }
}

export default PersonalInfoForm;

复制代码

这里我们定义了姓名和年龄输入框和提交按钮,然后处理输入框值变化的回调函数handleInputChange以及表单提交处理回调handleSubmit()

现在看来这个组件似乎没有什么问题,但是当我们的业务逻辑复杂起来,越来越多的Form组件加入进来之后,我们要为每个Form组件编写onChange和onSubmit的回调函数,以及表单验证处理。那么这时候我们就可以定义一个HOC组件来处理这些冗余的代码,实现一处定义多处使用。

Form HOC

新建一个Form.js文件,在里边定义form()函数,代码如下:

import React from "react";

export default function form(fields) {
  return WrappedForm => {
    return class extends React.Component {
      state = { ...fields };
      handleInputChange = event => {
        const target = event.target;
        const value = target.value;
        const name = target.name;

        this.setState({
          [name]: value
        });
      };

      handleSubmit = (event, onSubmit) => {
        event.preventDefault();
        // do any default validations and other stuff
        onSubmit();
      };

      render() {
        return (
          <WrappedForm
            onChange={this.handleInputChange}
            onSubmit={this.handleSubmit}
            fields={{ ...this.state }}
            {...this.props}
          />
        );
      }
    };
  };
}

复制代码

form()函数接收一个fields参数,用来保存被包裹的组件的表单项,然后返回了一个React Component,在这个组件中定义了state,初始值为被包裹组件传递进来的fields的值,还定义了handleInputChange和handleSubmit方法。这里handleSubmit中可以定义一些诸如表单验证逻辑,然后它接收一个函数,即被包裹组件的特定的表单处理逻辑,比如发送请求到后台API,或者更新某个组件状态等。

然后在render()中我们直接渲染了被包裹的组件的样式,然后把handleInputChange、handleSubmit、当前的state、还有pass through props(即HOC不需要,但是被包裹的组件需要的props)传递给了被包裹的组件。

使用HOC

让我们新建一个<PersonalInfoFormWithHOC>组件,代码如下:

import React from "react";
import form from "./Form";

function PersonalInfoFormWithHOC({ onChange, onSubmit, fields }) {
  const handleSubmit = event => {
    onSubmit(event, () => {
      console.log(fields);
    });
  };

  return (
    <form>
      <div>
        <label>
          Name:
          <input
            type="text"
            value={fields.name}
            name="name"
            onChange={onChange}
          />
        </label>
      </div>
      <div>
        <label>
          Age:
          <input
            type="number"
            value={fields.age}
            name="age"
            onChange={onChange}
          />
        </label>
      </div>
      <div>
        <button onClick={handleSubmit}>Submit</button>
      </div>
    </form>
  );
}

export default form({ name: "", age: 0 })(PersonalInfoFormWithHOC);

复制代码

因为有了HOC,我们不再需要state,所以这里定义成了函数式组件(functional component),然后通过HOC传递进来的props来处理表单数据。我们在最后export的时候调用了form HOC,把初始的表单项的值传递给了它,而我们能够在HOC传递回来的fields prop来动态的更新input的value。最后我们定义了组件特定的处理表单提交的逻辑,这里只为演示用,就打印了用户填写的表单值。

然后设想又有一个新的表单组件<EmployeeForm>加入进来,代码如下:

import React from "react";
import form from "./Form";

function EmployeeForm({ onChange, onSubmit, fields }) {
  const handleSubmit = event => {
    onSubmit(event, () => {
      console.log(fields);
    });
  };

  return (
    <form>
      <div>
        <label>
          Name:
          <input
            type="text"
            value={fields.name}
            name="name"
            onChange={onChange}
          />
        </label>
      </div>
      <div>
        <label>
          Position:
          <input
            type="text"
            value={fields.position}
            name="position"
            onChange={onChange}
          />
        </label>
      </div>
      <div>
        <label>
          Marital Status:
          <select name="mstatus" value={fields.mstatus} onChange={onChange}>
            <option value="">select</option>
            <option value="single">Single</option>
            <option value="married">Married</option>
          </select>
        </label>
      </div>
      <div>
        <button onClick={handleSubmit}>Submit</button>
      </div>
    </form>
  );
}

export default form({ name: "", position: "", mstatus: "" })(EmployeeForm);

复制代码

这里面我们只需定义特定的表单处理逻辑即可。而再新建form组件时,我们只关心如何布局表单项和表单处理逻辑即可。最后在index.js中引用这两个组件即可进行测试:

function App() {
  return (
    <div className="App">
      {/*<PersonalInfoForm /> */}
      <h1>Personal Info Form</h1>
      <PersonalInfoFormWithHOC />
      <h1>Employee Form</h1>
      <EmployeeForm />
    </div>
  );
}
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值