React HOC not displaying wrapped component - reactjs

There aren't many useful React Higher-Order-Component (HOC) examples out there that I could find and the simple ones that exists are so simplistic they do not really even scratch the surface of what can or cannot be done using HOC's. Anyway, from what I could find, I tried the following and I am sure I am mssing something (probably obvious) because this is my first attempt to try and use HOC's:
HOC:
import { Component, PropTypes } from 'react'
export const WizardWrapper = Step => class extends Component {
constructor(props) {
super(props)
this.nextPage = this.nextPage.bind(this)
this.previousPage = this.previousPage.bind(this)
this.state = {
page: 1
}
}
nextPage() {
this.setState({ page: this.state.page + 1 })
}
previousPage() {
this.setState({ page: this.state.page - 1 })
}
goToPage(page) {
this.setState({ page })
}
render() {
const { onSubmit, submitButtonText, step} = this.props
let hasNext = this.props.step > this.state.page
let hasPrev = this.state.page < this.props.step
let nextText = hasNext ? 'Next' : submitButtonText
return (
<Step previousPage={hasPrev ? this.previousPage : null}
onSubmit={hasNext ? this.nextPage : onSubmit}
{...this.state} {...this.props}/>
)
}
}
Some Component:
export const WizardPage = (props) => <h1>Some page</h1>
Attempt to display wrapped component (even if wrapping it does not actually do anything just yet):
import { Component, PropTypes } from 'react'
import {WizardWrapper} from 'common/client/components/wizard/WizardWrapper'
import {WizardPage} from 'common/client/components/wizard/WizardPage'
export default class FormTest extends Component {
render() {
let WrapperPage = WizardWrapper(WizardPage)
return (
<div>
{WrapperPage}
</div>
)
}
}
I don't get any errors, but nothing is displayed either. Any ideas what I am missing or doing wrong?
TIA!

In case anyone else runs into this, in my case I started seeing browser console logs with:
maxAge 50 1 VM611:1
maxAge 50 2 VM611:1
maxAge 50 3 VM611:1
I did some digging and found out these were likely coming from NPM's LRU cache, so I ran npm cache clean from the console. The errors went away and my HOC's finally started displaying as expected. Apparently a corrupt NPM cache was the culprit.

Related

The data that comes from an API end point is undefined in the child component in React

Good day to all!
I have this situation: I use Apollo client to get data from a GraphQL API endpoint in the parent class component in React. I pass this data to the child class component. The first time everything works fine but after a page refresh the data in the child component becomes undefined and the app crashes.
Here is the representation of the situation:
The ParentComponent
import React, { Component } from 'react'
import { gql } from "apollo-boost";
import {graphql} from 'react-apollo';
import ChildComponent from './ChildComponent'
const getProducts = gql`
{
category {
products {
id
name
gallery
}
}
}
`
class ParentComponent extends Component {
constructor(props) {
super(props)
this.state = {
products: []
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
products: [...this.props.data.category.products]
})
}, 1000)
}
render () {
let products = this.state.products;
return (
<div><ChildComponent theProducts = {products}/></div>
)
}
}
export default graphql(getProducts)(ParentComponent);
The ChildComponent
import React, { Component } from 'react'
class ChildComponent extends Component {
constructor(props) {
super(props)
this.state = {
products: this.props.theProducts
}
}
render () {
let item = this.state.products.find(each => each.id === id);
return (
<div>
<ul>
<li>{item.name}</li>
<li><img src= {item.gallery[0]} alt="product"></img></li>
</ul>
</div>
)
}
}
export default ChildComponent;
So, when the app starts everything seems to work fine. But if I refresh the page it throws an error and says that name is undefined, gallery is undefined. It is clear that the data is not coming through to the ChildComponent. Is there a way to make sure that the data comes in at any time?
Thank you in advance.
You use theProducts in the ChildComponent but you pass theProduct from ParentComponent . And state product also has the same error. Just update to theProducts and product

Cannot read property 'map' of undefined on react.js

Hello I'm a beginner of react, I'm stuck Cannot read the property 'map' of undefined, I understand is a type error please help me.
import React, { Component } from 'react';
import Message from "./Message";
class MessageList extends Component {
render() {
const { messages } = this.props;
return messages.map(message => (
<ul>
<Message message={message} />
</ul>
));
}
}
export default MessageList;
To u can try to use the "null object” pattern to avoid such errors when the prop is not uet to be sent to the component, the issue is not coming from the code you showed us so we cant help you find the main problem
Null object pattern Inside of the render method (also in this case is a default param):
const { messages = [] } = this.props;
As suggested messages can be null in which case map will throw an error.
However, you also haven't constructed props which means messages will always be null.
import React, { Component } from 'react';
import Message from "./Message";
class MessageList extends Component {
constructor(props) {
super(props);
// state if you need it.
this.state = { counter: 0 };
}
render() {
const { messages } = this.props;
return messages && messages.map(message => (
<ul>
<Message message={message} />
</ul>
));
}
}
export default MessageList;
Tested and this works.

Passing property dynamically to react component doesn't recognized by karma test

I'm trying to test whether a property that pass to a react component get the correct value, but it doesn't seems to work because (I think) the property get it's value dynamically depends on the parent component state. Here's the code (summarized):
import React from 'react';
import InfiniteScroll from 'react-infinite-scroller';
class myClass extends React.Component {
constructor(props) {
super(props);
this.state = {
....
inputChanged: 0
....
};
...
render() {
let items = [];
... code that retrieve items ...
return (
<InfiniteScroll
pageStart={this.state.inputChanged ? 1 : 0} // page start is changed according to this.state.inputChanged value
loadMore={this.loadMore.bind(this)}
hasMore={this.state.hasMoreItems}
loader={<div className="loader" key={0}>Loading ...</div>}
threshold={200}
>
<div className="container-fluid">
<div className="row">
{items}
</div>
</div>
</InfiniteScroll>
);
}
}
And in my spec file:
import React from 'react';
import {mount} from 'enzyme';
import {expect} from 'chai';
describe('myClass', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(
<myClass/>,
{attachTo: document.createElement('div')}
);
});
it('set correct pageStart', done => {
const InfiniteScroll = wrapper.find('InfiniteScroll');
wrapper.setState({
inputChanged: 1
});
expect(InfiniteScroll.props().pageStart).to.equal(1);
done();
});
}
But no matter what, InfinteScroll.props().pageStart is 0 and the test fails.
As far as I understand, it should changed accounring to wrapper.state().inputChanged, but it doesn't. Any ideas why?
Thanks in advance!
You are holding on to the reference of InfiniteScroll before calling setState. You need to move the code to get the reference of InfiniteScroll after the setState. Here is the updated test.
it('set correct pageStart', done => {
wrapper.setState({
inputChanged: 1
});
const InfiniteScroll = wrapper.find('InfiniteScroll');
expect(InfiniteScroll.props().pageStart).to.equal(1);
done();
});

Cannot read property 'map' of undefined in ReactJS

I've got a problem with my ReactJS App with getting data from api. I still have an error: 'Cannot read property 'map' of undefined', and I have no idea why it's happening.
My code:
UsersList.js
import React from 'react';
import ReactDOM from 'react-dom';
import fetch from 'isomorphic-fetch';
import { Card, Container, Icon } from 'semantic-ui-react'
import User from './User'
class ProfilesList extends React.Component {
constructor(props) {
super(props);
this.state = {
users: [],
fetched: false,
loading: false,
};
}
componentWillMount(){
this.setState({
loading : true
});
fetch('http://58be98154389c312007f403f.mockapi.io/users/users').then(res => res.json())
.then(res =>{
this.setState({
users : res.results,
loading : true,
fetched : true
});
});
}
render() {
const {fetched, loading, users} = this.state;
let content;
if(fetched){
content = <div>{this.state.users.map((user,index) =>
<User key={user.username} id={index+1} user={user}/>)}</div>;
}
else if(loading && !fetched){
content = <p> Loading ...</p>;
}
else{
content = (<div></div>);
}
return (
<div>
{content}
</div>
);
}
}
export default ProfilesList;
User.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Card, Container, Icon } from 'semantic-ui-react'
class User extends React.Component {
render() {
const {user, id} = this.props;
return (
<Card
image='http://semantic-ui.com/images/avatar/large/elliot.jpg'
header={user.username}
meta='Friend'
description='Elliot is a sound engineer living in Nashville who enjoys playing guitar and hanging with his cat.'
extra={(
<a>
<Icon name='user' />
16 Friends
</a>
)}
/>
);
}
}
export default User;
Thanks for your help!
Your state.users is undefined when you try to do this.state.users.map() in your render function. So task #1 is to figure out why your fetch() is returning undefined and fix that. It's a good idea to build in some handling for cases when you get undefined results or other errors and set your state appropriately. Additionally, I tend to check that an expected array is not undefined before I try to map it, like this:
{
expectedArray
?
expectedArray.map(someMappingFunction)
:
<div>expectedArray was 'undefined' or otherwise 'falsy'</div>
}
Such a conditional statement is called a "ternary" and is very useful because it can be embedded in JSX as an "if/else" statement. It has the form (condition) ? (expression if true) : (expression if false). For example:
var foo = 7;
var bar = (foo % 2 == 0) ? "Even" : "Odd";
console.log(bar); // "Odd"

forceUpdate is not re-rendering children

I'm using the react, redux react-router stack for my webapp. In the top level component's(the component that renders on the root path) componentDidMount I'm subscribing to the store as shown below
import NotificationsList from './components/notifier';
import React from 'react';
let Spinner = ({
isVisible,
showSpinner,
solidBackdrop
}) => (
<div style={{opacity: solidBackdrop ? 1 : 0.5}} className={"spinner " + (isVisible ? '' : 'hide')}></div>
);
export default class AppPage extends React.Component {
static contextTypes = {
store: React.PropTypes.object,
router: React.PropTypes.object
};
handleDismissNotification(notification) {
this.context.store.dispatch({
type: 'REMOVE_NOTIFICATION',
data: notification
});
}
componentDidMount() {
this.context.store.subscribe(() => this.forceUpdate());
}
render() {
let state = this.context.store.getState();
let props = {
notifications: state.notifications,
handleDismiss: this.handleDismissNotification.bind(this)
};
return (
<div className="'apppage-container">
{this.props.children}
<NotificationsList {...props} />
<Spinner isVisible={state.initialFetchInProgress || state.requestInProgress}
showSpinner={!state.initialFetchInProgress} solidBackdrop={state.initialFetchInProgress}/>
</div>
);
}
}
this.props.children here renders the component shown below
import Header from './components/header';
import React from 'react';
class ContentPage extends React.Component {
static contextTypes = {
store: React.PropTypes.object
};
render() {
let user = this.context.store.getState().user;
return <div className="content-container">
<Header user/>
</div>
}
}
export default ContentPage;
The problem is that when the first time a render happens, everything goes fine. Then when the render happens through forceUpdate, the child component is not getting re-rendered.
I think I got it. Every container component should be subscribed to the store separately. So accordingly, ContentPage should also have
componentDidMount() {
this.context.store.subscribe(() => this.forceUpdate());
}
As you replied to yourself, indeed the container component should subscribe to the store , but in addition to the subscription, it's good practice for the the container to also unsubscribe when unmounted :
componentDidMount() {
this.unsubscribe = this.context.store.subscribe(() => this.forceUpdate());
}
componentWillUnmount() {
this.unsubscribe();
}

Resources