React Higher Order Components - 構建可重用的業務邏輯

Higher Order Components(HOC) 的本質是Higher Order Functions,只不過返回值不是普通函數,而是React的component。利用HOC咱們能夠把類似的業務邏輯抽離出來,而後組件只負責渲染視圖和組件相關的業務邏輯,而公用的業務邏輯則使用HOC中定義好的公共邏輯,以實現代碼複用。本文將用兩個簡單的Form組件來演示HOC的基本使用方法。javascript

運行效果

CodeSandBox:java

Edit React HOC

建立Form組件

首先咱們來建立一個未使用HOC的Form組件:react

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()app

如今看來這個組件彷佛沒有什麼問題,可是當咱們的業務邏輯複雜起來,愈來愈多的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)傳遞給了被包裹的組件。ui

使用HOC

讓咱們新建一個<PersonalInfoFormWithHOC>組件,代碼以下:this

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。最後咱們定義了組件特定的處理表單提交的邏輯,這裏只爲演示用,就打印了用戶填寫的表單值。spa

而後設想又有一個新的表單組件<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>
  );
}
複製代碼
相關文章
相關標籤/搜索