React - 从高阶函数调用包装的组件回调

React - Call wrapped component callback from higher order function

我有一个包装服务调用的高阶函数。数据在回调中流式传输,我必须将其传递给包装的组件。目前我已经编写了下面的代码,其中 child 将 handleChange 分配给 HOC 传递的空 object。包装的组件是一个常规的 JS 网格,因此我必须调用 api 来添加数据而不是将其作为道具传递。 有更简洁的方法吗?

    function withSubscription(WrappedComponent) {     
    return class extends React.Component {
      constructor(props) {
        super(props);       
        this.handler = {};
      }

      componentDidMount() { 
        DataSource.addChangeListener(this.handleChange);
      }

      componentWillUnmount() {
        DataSource.removeChangeListener(this.handleChange);
      }

      handleChange(row) {
        if (typeof this.handler.handleChange === "function") {
            this.handler.handleChange(row);
        }
      }

      render() { 
        return <WrappedComponent serviceHandler={this.handler} {...this.props} />;
      }
    };
  }

  class MyGrid extends React.Component {
     constructor(props) {
         super(props);
         if (props.serviceHandler !== undefined) {
             props.serviceHandler.handleChange = this.handleChange.bind(this);
         }

         this.onReady = this.onReady.bind(this);
     }

     onReady(evt) {
         this.gridApi = evt.api;
     }

     handleChange(row) {
        this.gridApi.addRow(row);
     }

     render() { 
        return <NonReactGrid onReady={this.onReady} />;
      }

  }

  const GridWithSubscription = withSubscription(MyGrid);

包装组件应该注意 handler.handleChange 看起来很尴尬。

如果 withSubscription 可以限制为仅使用有状态组件,组件可能会公开 changeHandler 挂钩:

function withSubscription(WrappedComponent) {
  return class extends React.Component {
    ...
    wrappedRef = React.createRef();

    handleChange = (row) => {
      if (typeof this.wrappedRef.current.changeHandler === "function") {
        this.wrappedRef.current.changeHandler(row);
      }
    }

    render() { 
      return <WrappedComponent ref={this.wrappedRef}{...this.props} />;
    }
  };
}

class MyGrid extends React.Component {
  changeHandler = (row) => {
    ...
  }
}

const GridWithSubscription = withSubscription(MyGrid);

要使用有状态和无状态组件 withSubscription 应该更通用化以通过 props 与包装组件交互,即注册回调:

function withSubscription(WrappedComponent) {
  return class extends React.Component {
    handleChange = (row) => {
      if (typeof this.changeHandler === "function") {
        this.changeHandler(row);
      }
    }

    registerChangeHandler = (cb) => {
      this.changeHandler = cb;
    }

    render() { 
      return <WrappedComponent
        registerChangeHandler={this.registerChangeHandler}
        {...this.props}
      />;
    }
  };
}

class MyGrid extends React.Component {
  constructor(props) {
    super(props);
    props.registerChangeHandler(this.changeHandler);
  }
  changeHandler = (row) => {
    ...
  }
}

如果应用程序已经使用某种形式的事件发射器,如 RxJS 主题,则可以使用它们代替 handler.handleChange 在父子之间进行交互:

function withSubscription(WrappedComponent) {
  return class extends React.Component {
    changeEmitter = new Rx.Subject();

    handleChange = (row) => {
      this.changeEmitter.next(row);
    }

    render() { 
      return <WrappedComponent
        changeEmitter={this.changeEmitter}
        {...this.props}
      />;
    }
  };
}

class MyGrid extends React.Component {
  constructor(props) {
    super(props);
    this.props.changeEmitter.subscribe(this.changeHandler);
  }

  componentWillUnmount() {
    this.props.changeEmitter.unsubscribe();
  }

  changeHandler = (row) => {
    ...
  }
}

为此目的传递 subjects/event 发射器在 Angular 中很常见,因为框架已经强加了对 RxJS 的依赖。