在不更改JSX中的组件名称的情况下渲染HOC(组件)

问题描述 投票:1回答:2

我有两个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(<Component />)) }这引起了我的错误说法

函数作为React子函数无效。如果从渲染中返回Component而不是<Component />,则可能会发生这种情况。

  1. 在渲染之前创建一个变量来存储HOC组件: const HOC = withContextOne(Component)

然后简单地用<HOC {...props}/>等渲染。我不喜欢这个方法,因为它改变了我的JSX中组件的名称

reactjs
2个回答
1
投票

您可以使用装饰器来缓解链式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';

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;
};

这将把<WithContextOneHOC(YourComponentHere)>放在你的React树中,而不仅仅是通用的React <Component>元素。

© www.soinside.com 2019 - 2024. All rights reserved.