【问题标题】:Render HOC(Component) without changing Component Name in JSX在不更改 JSX 中的组件名称的情况下渲染 HOC(组件)
【发布时间】:2019-02-10 20:17:59
【问题描述】:

我有两个向组件添加上下文的 HOC,如下所示:

const withContextOne = Component => class extends React.Component {
  render() {
    return (
      <ContextOne.Consumer>
        {context => <Component {...this.props} one={context} /> }
      </ContextOne.Consumer>
    );
  }
};
export default withContextOne;

期望的结果

我只是想要一种语法简洁的方式来用这个 HOC 包装一个组件,这样它就不会对我的 JSX 结构产生太大影响。

我尝试过的

  1. 导出附加了 HOC 的组件export default withContextOne(withContextTwo(MyComponent)) 这种方式最简洁,但不幸的是它破坏了我的单元测试。
  2. 尝试从 JSX 中评估 HOC,例如:

    { withContextOne(withContextTwo(&lt;Component /&gt;)) } 这给我一个错误说

函数作为 React 子级无效。如果您从渲染返回一个组件而不是 ,则可能会发生这种情况。

  1. 在渲染之前创建一个变量来存储 HOC 组件:

    const HOC = withContextOne(Component)

然后简单地使用&lt;HOC {...props}/&gt; 等进行渲染。我不喜欢这种方法,因为它会更改我的 JSX 中组件的名称

【问题讨论】:

  • 尝试装饰器?
  • @DanielLizik 非常好,如果您发布答案,我会接受 - 使用装饰器方法谢谢。

标签: reactjs


【解决方案1】:

您可以在返回包装的组件之前设置displayName

const withContextOne = Component => {
  class WithContextOneHOC extends React.Component {
    render() {
      return (
        <ContextOne.Consumer>
          {context => <Component {...this.props} one={context} /> }
        </ContextOne.Consumer>
      );
    }
  }

  WithContextOneHOC.displayName = `WithContextOneHOC(${Component.displayName})`;

  return WithContextOneHOC;
};

这会将&lt;WithContextOneHOC(YourComponentHere)&gt; 放入你的 React 树中,而不仅仅是通用的 React &lt;Component&gt; 元素。

【讨论】:

  • 有趣的是我不知道你能做到这一点。
【解决方案2】:

您可以使用装饰器来缓解链式 HOC 的语法痛苦。我忘记了您需要哪个特定的 babel 插件,它可能(仍然)是 babel-plugin-transform-decorators-legacy 或可能是 babel-plugin-transform-decorators,具体取决于您的 babel 版本。

例如:

import React, { Component } from 'react';
import { withRouter } from 'react-router';
import { injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { resizeOnScroll } from './Resize';

@withRouter
@resizeOnScroll
@injectIntl
@connect(s => s, (dispatch) => ({ dispatch })) 
export default class FooBar extends Component {
  handleOnClick = () => {
    this.props.dispatch({ type: 'LOGIN' }).then(() => {
      this.props.history.push('/login');
    });
  }

  render() {
    return <button onClick={}>
      {this.props.formatMessage({ id: 'some-translation' })}
    </button>
  }
}

但是,装饰器需要注意的是,测试变得很痛苦。你不能使用带有const 的装饰器,所以如果你想导出一个“干净的”未装饰的类,那你就不走运了。这是我现在通常做的,纯粹是为了测试:

import React, { Component } from 'react';
import { withRouter } from 'react-router';
import { injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { resizeOnScroll } from './Resize';

export class FooBarUndecorated extends Component {
  handleOnClick = () => {
    this.props.dispatch({ type: 'LOGIN' }).then(() => {
      this.props.history.push('/login');
    });
  }

  render() {
    return <button onClick={}>
      {this.props.formatMessage({ id: 'some-translation' })}
    </button>
  }
}

export default withRouter(
  resizeOnScroll(
    injectIntl(
      connect(s => s, ({ dispatch }) => ({ dispatch }))(
        FooBarUndecorated
      )
    )
  )
);

// somewhere in my app
import FooBar from './FooBar';

// in a test so I don't have to use .dive().dive().dive().dive()
import { FooBarUndecorated } from 'src/components/FooBar';

【讨论】:

  • 是的,这很棒,但不幸的是,昨天花了所有时间实现装饰器,却发现它们在我当前的应用程序中无法测试。为了使它们起作用,需要进行太多更改。我认为将不得不回到大量的样板html。无论如何感谢您的帮助
猜你喜欢
  • 2020-08-18
  • 2016-07-13
  • 1970-01-01
  • 1970-01-01
  • 2019-04-28
  • 2021-03-19
  • 2021-09-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多