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

时间:2018-10-12 07:09:53

标签: reactjs higher-order-functions

我有一个包装服务调用的高阶函数。数据在回调中流式传输,我必须将其传递给包装的组件。我目前在下面编写了代码,其中子代将handleChange分配给HOC传递的空对象。包装的组件是常规的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);

1 个答案:

答案 0 :(得分:2)

该包装好的组件应该知道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,应使其更通用,以便通过道具与包装的组件进行交互,即注册一个回调:

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) => {
    ...
  }
}

为此目的而传递主题/事件发射器在Angular中很常见,因为该框架已经强加了对RxJS的依赖。