如何在调用高阶组件的同时访问类之外的prop,以使高阶组件可重用

时间:2018-07-23 10:09:57

标签: reactjs react-native higher-order-functions higher-order-components

如果没有可用的数据,我已经创建了更高阶的组件来渲染加载器。名为 Loader 的组件是高阶组件。而名为 ContactList 的组件将调用“高级”组件。 我的ContactList组件代码:

import React, { Component } from 'react';
import '../App.css';
import Loader from './hoc/Loader.js';

class ContactList extends Component {
  constructor(props) {
    super(props);
    this.state = {
    }
  }

 render() {
   return (
     <div className="App">
       {
        this.props.contacts.map((contact, index) => (
          <div key={index}>
            <p>{contact.name}</p>
          </div>
        ))
      }
    </div>
   );
  }
}

export default Loader(this.props.contacts)(ContactList);

Loader(HOC)组件的代码-

import React, { Component } from 'react';
import './Loader.css';

const Loader = (propName) => (WrappedComponent) => {
    return class Loader extends Component {
        isEmpty(prop) {
            return (
                prop === null ||
                prop === undefined ||
                (prop.hasOwnProperty('length') && prop.length === 0) ||
                (prop.constructor === Object.keys(prop).length === 0)
            );
        }
        render() {
            return this.isEmpty(propName) ? <div className="loader"></div> : <WrappedComponent {...this.props}/>
        }
    }
}

export default Loader;

但是我收到此错误

  

未捕获的TypeError:无法读取未定义的属性“联系人”   因为道具在课外无法访问。

我尝试了许多解决方案-

1)one of tried solution

2)如果我将导出语句更改为export default Loader(ContactList),则可以正常工作,但加载器组件不再可重用

5 个答案:

答案 0 :(得分:1)

这里:

export default Loader(this.props.contacts)(ContactList);

您正在尝试在课外使用道具。无法从ContactList类中访问它们。

编辑

如果您需要显示加载程序,则取决于联系人数组,则无需使用HOC。只需在模板内部使用Loader:

<Loader contacts={this.props.contacts}>
      {
        this.props.contacts.map((contact, index) => (
          <div key={index}>
            <p>{contact.name}</p>
          </div>
        ))
      }
</Loader>

注意,在这种情况下,您必须使Loader只是一个组件,而不是HOC

以下是这种情况的示例:

class Loader extends React.Component {
  render(){
    const {values} = this.props;
    if (values.length) {
       return this.props.children;
    }
    return <div>Loading...</div>
  }
 }

并在ContactListComponent中使用它:

import React, { Component } from 'react';
import '../App.css';
import Loader from './hoc/Loader.js';

class ContactList extends Component {
  constructor(props) {
    super(props);
    this.state = {
    }
  }

 render() {
   return (
     <div className="App">
       <Loader values={this.props.contacts}>
       {
        this.props.contacts.map((contact, index) => (
          <div key={index}>
            <p>{contact.name}</p>
          </div>
        ))
      }
      </Loader>
    </div>
   );
  }
}

export default ContactList;

Loader组件只是示例,请使用您自己的Loader。 就像我说过的那样,无需使用HOC。无论如何,您的Loader组件将是可重用的,因为您也可以在其他组件中使用它。希望这会有所帮助

答案 1 :(得分:0)

问题似乎是您尝试访问道具的方式:

export default Loader(this.props.contacts /* <-- this is incorrect */ )(ContactList);

重新考虑情况-您要达到的目标是什么? this在模块上下文中是什么意思?

通常,在使用HOC时,您会发现出现类似的模式:

export default Loader(ContactList);

然后(取决于Loader的实现)将允许您导入该组件(即LoaderContactList)并将道具“穿过” Loader向下传递到内部ContactList,如下所示:

<LoaderContactList contacts={ [ 'bob', 'joe', 'mary' ] } />

希望这可以提供一些澄清。

更新说明了HOF / HOC概念

要说明我的答案中描述的HOC概念,请参阅以下内容:

export function(InnerComponent) { // This is the higher-order-function to export (ie Loader)

    return class OuterComponent extends Component { // This is the higer-order-component

        render() {
            /* To illustrate the possiblitiy of reusable code via HOC's, 
               this HOC responds to an generic 'isBusy' prop by returning 
               a loading message if truthy */
            if(this.props.isBusy) {
                return <p>Loading</p>
            }

            /*
            Otherwise, the inner component that you pass to the higer- 
            order-function is rendered. The "pass through" concept I 
            discuss is shown here. Notice how all props from the 
            OuterComponent are passed through to the InnerComponent (ie 
            ContactList) 
            */
            return <InnerComponent { ...this.props } />
        }
    }
}

答案 2 :(得分:0)

导出默认的加载程序(this.props.contacts)(ContactList);

这是错误的,您必须在类的外部设置一个let变量并将其设置在类的内部,然后在其中调用

答案 3 :(得分:0)

您的代码应如下所示

HOC:

const Loader = (props) => (WrappedComponent) => (moreProps) => {
const ContactList = props.ContactList;
 return (
   <Wrapper>
     <Container>
       <WrappedComponent {...moreProps} />
     </Container>
   </Wrapper>
 )
}

现在您可以像这样使用它

export default Loader({ [ 'Praveen', 'Hari', 'Swamy' ] })(ContactList)

答案 4 :(得分:0)

我更改了传递和访问数据的方式。我更改的代码如下-

ContactList组件的代码

import React, { Component } from 'react';
import '../App.css';
import Loader from './hoc/Loader.js';

class ContactList extends Component {
  constructor(props) {
    super(props);
    this.state = {
    }
  }

  render() {
    return (
      <div className="App">
        {
          this.props.contacts.map((contact, index) => (
            <div key={index}>
              <p>{contact.name}</p>
            </div>
          ))
        }
      </div>
    );
  }
}

export default Loader('contacts')(ContactList);

加载程序的代码(高级组件)-

import React, { Component } from 'react';
import './Loader.css';

const Loader = (propName) => (WrappedComponent) => {
    return class Loader extends Component {
        isEmpty(prop) {
            return (
                prop === null ||
                prop === undefined ||
                (prop.hasOwnProperty('length') && prop.length === 0) ||
                (prop.constructor === Object.keys(prop).length === 0)
            );
        }
        render() {
            return this.isEmpty(this.props[propName]) ? <div className="loader"></div> : <WrappedComponent {...this.props}/>
        }
    }
}

export default Loader;