Reputation: 603
I have created higher order component to render loader, if data is not available. component named as Loader is higher order component. and component named as ContactList calls the Higher order component. My code for ContactList component:
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);
code for Loader(HOC) component -
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;
But i am getting this error
Uncaught TypeError: Cannot read property 'contacts' of undefined as props are not accessible outside of class.
I have tried many solutions -
2) If I change my export statement to export default Loader(ContactList)
, then this works fine but the loader component is no more reusable
Upvotes: 3
Views: 2649
Reputation: 603
I changed the way of passing and accessing data. My changed code is as follow -
Code for ContactList component
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);
Code for Loader (either Higher order component) -
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;
Upvotes: 0
Reputation: 30360
The problem appears to be the way you're trying to access props:
export default Loader(this.props.contacts /* <-- this is incorrect */ )(ContactList);
Rethink the situation - what is it you're trying to achieve? What does this
mean in the context of your module?
Usually when using HOC's, you will find that a pattern similar to this emerges:
export default Loader(ContactList);
Which (depending on the implementation of Loader
) would then allow you to import that component (ie LoaderContactList) and pass props "through" Loader
down to the inner ContactList
as follows:
<LoaderContactList contacts={ [ 'bob', 'joe', 'mary' ] } />
Hopefully this offers some clarification.
To illustrate the HOC concept described in my answer, see the following:
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 } />
}
}
}
Upvotes: 0
Reputation: 819
Here:
export default Loader(this.props.contacts)(ContactList);
You're trying to access props outside of class. It is impossible to access them out of your ContactList class.
EDIT
If you need to show loader, depending on contacts array, you don't need to use HOC. Just use Loader inside of your template:
<Loader contacts={this.props.contacts}>
{
this.props.contacts.map((contact, index) => (
<div key={index}>
<p>{contact.name}</p>
</div>
))
}
</Loader>
Note, that in that case, you have to make your Loader just a component, not HOC.
Here the example of such case:
class Loader extends React.Component {
render(){
const {values} = this.props;
if (values.length) {
return this.props.children;
}
return <div>Loading...</div>
}
}
And use it in 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;
The Loader component just example, use your own Loader. As I've said, there are no need to use HOC. Anyway, your Loader component will be reusable, because you can use it in different component also. Hope this helps
Upvotes: 1
Reputation: 2860
Your code should look like this
HOC :
const Loader = (props) => (WrappedComponent) => (moreProps) => {
const ContactList = props.ContactList;
return (
<Wrapper>
<Container>
<WrappedComponent {...moreProps} />
</Container>
</Wrapper>
)
}
Now you can use it like this
export default Loader({ [ 'Praveen', 'Hari', 'Swamy' ] })(ContactList)
Upvotes: 0
Reputation: 253
export default Loader(this.props.contacts)(ContactList);
this is wrong , you have to set one let variable out side of class and set it inside of class , then call it there
Upvotes: 0