【问题标题】:HOC pass through propertiesHOC 传递属性
【发布时间】:2019-04-02 08:21:51
【问题描述】:

我最近在处理复杂的 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。

我在做什么有什么问题吗?有更好的方法吗?我错过了什么吗?

【问题讨论】:

    标签: reactjs


    【解决方案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。我对它们还不是很熟悉,但它们倾向于更有效地解决组件的可重用性。

    【讨论】:

    • 我选择了 Render Props 解决方案,感谢您的提示 :)
    【解决方案2】:

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

    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
    

    【讨论】:

    • 是的,我也有同样的想法,但我使用组件 propTypes 仅通过 HOC 所需的道具
    猜你喜欢
    • 1970-01-01
    • 2018-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多