【问题标题】:How to access prop outside of class while calling higher order component such that Higher order component will be reusable如何在调用高阶组件时访问类外的道具,以便高阶组件可重用
【发布时间】:2018-12-30 18:12:23
【问题描述】:

如果数据不可用,我已经创建了更高阶的组件来渲染加载器。 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;

但是我收到了这个错误

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

我尝试了很多解决方案 -

1) one of tried solution

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

【问题讨论】:

  • 我们可以将contacts有一个prop传递给ContactList对吗?
  • 是的,contactList 组件正在通过 props 获取联系人数组。现在我想将它发送到高阶组件

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


【解决方案1】:

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

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;

【讨论】:

  • 假设您有 this.props.anyName,将其作为export default Loader('anyName')(ContactList) 传递并作为this.props[propName] 访问它。这样,HOC 仍然是可重用的
【解决方案2】:

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

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

【讨论】:

  • 是的,这可行,但我不能通过这种方法使组件可重用
  • this.props.isBusy 的值是多少?
  • 感谢您的回复。我想知道如何从我们调用 HOC 的组件发送 isBusy。这应该是在道具方面,以便它能够检测到数据的变化
  • 能否检测到联系人数组的变化?因为最初它是空的,并且在 api 响应之后它具有价值
  • 你可以实现在内部检测数据变化的逻辑,但是因为你的数组中的变化会立即呈现在你的联系人组件中,这使得 HOC 的这个应用程序变得多余。如果您正在异步加载联系人数据,那么您可以在加载联系人数据时将 isBusy 设置为 true,从而实现可重用组件的目标
【解决方案3】:

这里:

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

您正试图在课堂外访问道具。从 ContactList 类中访问它们是不可能的。

编辑

如果需要显示加载器,根据contacts数组,不需要使用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 组件将是可重用的,因为您也可以在不同的组件中使用它。希望这会有所帮助

【讨论】:

  • @Azamat Zorkanov,我知道我正在课外访问道具,所以出现了这个错误。但我需要解决这个问题。有什么办法吗?
  • 我的 Loader 组件如何找到天气数据是否可用
  • 如果你将数据传递给Loader,那么它会通过props找到关于数据存在的信息
  • @Azamat Zorkanov,是的,我可以将 HOC 称为export default Loader(ContactList);,这将起作用,但加载器组件将无法重用。
【解决方案4】:

您的代码应如下所示

HOC:

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

现在可以这样使用了

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

【讨论】:

  • 感谢@Praveen Rao Chavan.G。这将起作用,但 Loader 组件将不可重用
  • 所以您的意思是 Loader 是一个 HOC,您不仅可以将其用于联系人,还可以用于其他类型的数据,对吧?
  • 是的,我想让它可重用,这样我就可以在我的应用程序的任何地方使用它来处理不同的组件
【解决方案5】:

导出默认加载器(this.props.contacts)(ContactList);

这是错误的,你必须在类外设置一个 let 变量并将其设置在类内,然后在那里调用它

【讨论】:

    猜你喜欢
    • 2017-02-14
    • 2018-04-03
    • 2016-12-08
    • 2021-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-27
    • 2019-10-31
    相关资源
    最近更新 更多