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

时间:2018-09-05 15:59:13

标签: reactjs

我有两个将上下文添加到组件的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而不是,则可能会发生这种情况。

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

    const HOC = withContextOne(Component)

然后仅使用<HOC {...props}/>等进行渲染。我不喜欢这种方法,因为它会更改JSX中组件的名称

2 个答案:

答案 0 :(得分:1)

您可以在返回包装的组件之前设置<html> <head> <title></title> <style type="text/css" media="screen"> </style> <script type="text/javascript" src="jquery-1.11.3.min.js"></script> <script type="text/javascript"> $(function () { $.get('inputfile.txt', function(data) { //If the returned data is not empty or whitespace if (data) { // split the string into several strings when a new line occures var lines = data.split('\n'); // Take note of the \n This is the new line character also known as a line ending, end of line (EOL), or line break. // If there are more than 0 lines if (lines.length > 0) { // For every line console.log out what the line is with the line number prepended. var $content = $('#content'); //grab the content area for (var i = 0; i < lines.length; i++) { console.log(i + ": " + lines[i]); $content.html($content.html() + "\n" + i + ": " + lines[i]); //insert a new line into the content area } } } else { // It is empty, do somethin else alert('no data present in txt file.'); } }); }); </script> </head> <body> <pre id="content"></pre><!-- Added a place for the content --> </body> </html>

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; }; 放入您的React树中,而不仅仅是一般的React <WithContextOneHOC(YourComponentHere)>元素。

答案 1 :(得分:1)

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

例如:

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