HOC直通属性

时间:2018-10-29 11:48:20

标签: reactjs

我最近在为复杂的HOC而苦苦挣扎,我如何只能通过其中定义的新道具而不是其他任何道具。 更准确地说,假设我的HOC利用其他扩展其属性的HOC,例如

const withSession = (WrappedComponent) => {

  class SessionProvider extends React.PureComponent {
    constructor(props) {
      super(props);
      this.login = this.login.bind(this);
    }

    login() {
      console.log('login'); // this will dispatch some action
      // this.props.dispatch... 
    }

    render() {

      return (
        <WrappedComponent
          doLogin={this.login}
          {...this.props} 
        />
      );
    }
  }

  const mapStateToProps = null;

  function mapDispatchToProps(dispatch) {
    return {
      dispatch,
    };
  }

 const withConnect = connect(mapStateToProps, mapDispatchToProps);

 return compose(
    withConnect,
    withRouter,
  )(injectIntl(SessionProvider));
};

此处SessionProvider使用dispatchinjectIntl将属性附加到其props。但是,我不想将这些道具传递给包装的组件。这个想法是要有一个SessionProvider HOC,它具有一些API调用,但仅使用login扩展了包装的组件。 我注意到,如果保留{...this.props},则被包装的组件还将获得HOC所使用的所有props,我不想通过。 因此,我想通过更改HOC渲染方法来分解this.props来明确定义要传递的属性:

render() {
  const { dispatch, intl, ...otherProps } = this.props;
  return <WrappedComponent doLogin={this.login} { ...otherProps} />;
}

但是,如果WrappedComponent本身具有dispachintl道具,则这些道具不会通过HOC传递。

我的工作有什么问题吗?有更好的方法吗?我想念什么吗?

2 个答案:

答案 0 :(得分:1)

您所做的没有错。使用HOC时,道具名称冲突是一个已知问题。因此,据我所知,您可以使用的最佳替代方法是 Render Props 模式,该模式有助于使组件render尽可能保持声明性。对于您的情况,请考虑以下内容:

class Session extends React.PureComponent {
  constructor(props) {
    super(props);
    this.login = this.login.bind(this);
  }

  login() {
    console.log("login"); // this will dispatch some action
    // this.props.dispatch...
  }

  // ...

  render() {
    return (
      <React.Fragment>
        {this.props.children({
          doLogin: this.login
          doLogout: this.logout
          // ...
        })}
      </React.Fragment>
    );
  }
}

// ...

return compose(
  withConnect,
  withRouter
)(injectIntl(Session));

并从其他组件中使用它:

// ...
render() {
  return (

    <Session>
      {({ doLogin, doLogout }) => (
        <React.Fragment>
          <SomeComponent doLogin={doLogin} />
          <button onClick={doLogout}>Logout</button>
        </React.Fragment>
      )}
    </Session>

  )
}

更新:

v16.7.0-alpha 中有一个非常有前途的 Hooks Proposal 。我对它们还不太熟悉,但是它们往往可以更有效地解决组件的可重用性。

答案 1 :(得分:0)

您需要复制静态属性,为此我在下面的代码中使用..您可以根据需要添加更多属性

export const REACT_STATICS = {
  childContextTypes: true,
  contextTypes: true,
  defaultProps: true,
  displayName: true,
  getDefaultProps: true,
  mixins: true,
  propTypes: true,
  type: true
};

export const KNOWN_STATICS = {
  name: true,
  length: true,
  prototype: true,
  caller: true,
  arguments: true,
  arity: true
};

export function hoistStatics(targetComponent, sourceComponent) {
  var keys = Object.getOwnPropertyNames(sourceComponent);
  for (var i = 0; i < keys.length; ++i) {
    const key = keys[i];
    if (!REACT_STATICS[key] && !KNOWN_STATICS[key]) {
      try {
        targetComponent[key] = sourceComponent[key];
      } catch (error) {}
    }
  }
  return targetComponent;
}


// in HOC
const hoistedSessionProvider = hoistStatics(SessionProvider, WrappedComponent);

// use hoistedSessionProvider in compose