I build a HOC and wanted to wrap it withStyles. But then I get the following error. What is wrong?
TypeError: Cannot call a class as a function
react js
const withLoader = (loadingProp) => (WrappedComponent) => {
return class LoadIndicator extends Component {
render() {
return <h1>hello world</h1>
}
}
}
export default withStyles(styles)(withLoader)
Functional components return should always be like render function return value is in class components.
Following modification to your code should fix your error.
class LoadIndicator extends Component {
render() {
return <h1>hello world</h1>
}
}
const withLoader = (loadingProp) => (WrappedComponent) => {
return <LoadIndicator />
}
export default withStyles(styles)(withLoader(loadingProp))
This loader HOC needs to be instantiated with the prop name of the loading flag.
I'm going to assume the styles HOC is for LoadIndicator:
const withLoader = (loadingProp) => (WrappedComponent) => {
class LoadIndicator extends Component {
render() {
// Todo: render WrappedComponent and/or a loading element.
return <h1>hello world</h1>
}
}
return withStyles(styles)(LoadIndicator);
}
export default withLoader;
Now when you use this HOC you still need to specify what the loading property is called:
withLoader('loading')(SomeComponent)
You should wrap the returned component instead of the HOC function:
const withLoader = (loadingProp) => (WrappedComponent) => {
return withStyles(styles)(class LoadIndicator extends Component {
render() {
return <h1>hello world</h1>
}
}}
}
export default (withLoader)
Related
Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
import './App.css';
import React, { Component } from 'react';
const OOO = () => {
//console.log(this.props.children)
return class extends Component {
render() {
return (
<Rem {...this.props} />
);
}
}
}
class Rem extends Component {
render() {
return (
<p>Helo</p>
)
}
}
export default OOO;
This may happen if you return a Component instead of <Component /> from render
That is exactly what you are doing here. Calling <OOO /> returns a class instead of a JSX element.
This isn't really a higher-order component because you are not taking the inner component Rem as an argument. Did you intend to? That would look something like this:
const withOOO = (InnerComponent) => {
return class extends Component {
render() {
return (
<InnerComponent {...this.props} />
);
}
}
}
class Rem extends Component { ... }
export default withOOO(Rem);
If this is just a component that uses Rem, not an HOC, then you don't need to create a new class.
const OOO = (props) => {
return <Rem {...props} />;
};
class Rem extends Component { ... }
export default OOO;
I think you used the function wrong, the function OOO returns a class and that class you can use. I have no idea why you would want to do this but here is how you can use the HOC:
//your code in a file called OOO.js
import React, { Component } from 'react';
const OOO = () => {
//you cannot log this.props here, it is not in the class
//console.log(this.props.children)
//I think you have to name the class but I'm not sure
//Have not used classes in React for quite some time
//even React documentation lists a large amount of
//disadvantages using classes and it's only around for
//legacy code
return class MyComponent extends Component {
render() {
//here you can log this.props
//console.log(this.props.children)
return <Rem {...this.props} />;
}
};
};
class Rem extends Component {
render() {
return <p>Helo</p>;
}
}
export default OOO;
//end of the OOO.js file
//here is how you can use it
import CreateComponent from 'OOO.js';
const Component = CreateComponent();
const MyComponent = ()=><Component />
export default MyComponent;
In React can methods be passed to {this.children} in a container consumer model. What I mean to ask is I have a provider component and I need to pass or refer the provider components methods in the child component.
export default class ContainerCompo extends React.Component {
constructor(props) {
super(props);
this.myHocComponent = null;
}
methodOne() {
//some code
}
methodTwo() {
//some code
}
render() {
return (
{this.props.children}
}
}
export default class InputComponent extends React.Component {
constructor(props) {
super(props);
this.myHocComponent = null;
}
validate() {
ContainerCompo.methodOne(param)
}
render() {
return <InputComponent />
}
// Rendering the components
<ContainerCompo>
<InputComponent containerMethods={methods of ContainerCompo}/>
</ContainerCompo>
I hope my question is clear here, please suggest
First create a react context.
import React, { Component, createContext } from 'react';
// Create's authentication context to be use anywhere in the app
const ContainerContext = createContext();
export default ContainerContext;
Then create a provider for it.
export default class ContainerProvider extends Component {
constructor(props) {
super(props);
this.myHocComponent = null;
}
methodOne() {
//some code
}
methodTwo() {
//some code
}
render() {
const { children } = this.props;
return (
<ContainerContext.Provider
value={{
container: {
methodOne: (...params) => this.methodOne(...params),
methodTwo: (...params) => this.methodTwo(...params)
}
}}
>
{children}
</ContainerContext.Provider>
)}}
Wrap your App with the provider.
import ContainerProvider from './ContainerProvider'
<ContainerProvider>
<App />
</ContainerProvider>
Then create a consumer for the context
export default function withContainer(InComponent) {
return function ContainerComponent(props) {
return (
<ContainerContext.Consumer>
{({ container }) => <InComponent {...props} container={container} />}
</ContainerContext.Consumer>
);
};
}
Then import the consumer and user in your components and you will get the methods as props
import withContainer from './ContainerConsumer'
render() {
const { container } = this.props;
return(<div />)
}
export default withContainer(YourComponent);
I am a beginner in React. Looking at a few medium articles and React docs(which is complicated) I have tried to implement this very basic Context API.
I have missed some basic point which is the reason why I haven't got the correct result which is to pass data through the components tree and access them in the child component.
Please let me know how to correct given code snippet and what have I missed.
import React from 'react';
import './index.css';
const AppContext = React.createContext();
function GreenBox () {
return <div className='green-box'>
<AppContext.Consumer>
{(context) => context.value}
</AppContext.Consumer>
</div>
}
function BlueBox () {
return <div className='blue-box'><GreenBox/></div>
}
class RedBox extends React.Component {
render() {
return <div className='red-box'>
<AppContext.Consumer>
{(context) => context.value}
</AppContext.Consumer>
<BlueBox/>
</div>
}
}
class Context extends React.Component {
state = {
number: 10
}
render() {
return (
<AppContext.Provider value = {this.state.number}>
<RedBox/>
</AppContext.Provider>
)
}
}
export default Context;
The value you set in the Provider will be the argument received in the render props function in Consumer, so instead of accessing the number you're expecting with context.value, you should just change to context.
I'm using the connect function React Redux to connect my store to a React component. After it is connected I want to render the component. This works:
class Init extends React.Component {
render() {
return (
<View /> // Is really more elaborate
)
}
}
Connect it to the store:
const InitPage = connect(
state => ({
}),
dispatch => ({
})
)(Init)
Now render it:
class App extends React.Component {
render() {
const content = <InitPage />
return (
{content}
)
}
}
Great. This all works. However...
When I place the connect inside a function and return the connect, as in:
const getInitPage = () => {
const InitPage = connect(
state => ({
}),
dispatch => ({
})
)(Init)
return InitPage
}
If I now try to render the component:
class App extends React.Component {
render() {
const content = getInitPage()
return (
{content}
)
}
}
I get an error:
Invariant Violation: React.Children.only expected to receive a single React element child.
What is the correct way to return a Redux "connected" component from a function?
In case you are returning the component from the function, you need to render it like
class App extends React.Component {
render() {
const Content = getInitPage()
return (
<Content/>
)
}
}
Also make sure the componentName starts with a uppercase character.
I have a scenario where I want to create an HOC that detects mouse events (e.g. mouseenter, mouseleave) when they occur on the HOC's WrappedComponent, then pass the WrappedComponent a special prop (e.g. componentIsHovered). I got this working by using a ref callback to get the wrapped component instance, then adding event listeners to the wrapped instance in my HOC.
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
export default (WrappedComponent) => {
return class DetectHover extends Component {
constructor(props) {
super(props)
this.handleMouseEnter = this.handleMouseEnter.bind(this)
this.handleMouseLeave = this.handleMouseLeave.bind(this)
this.bindListeners = this.bindListeners.bind(this)
this.state = {componentIsHovered: false}
this.wrappedComponent = null
}
componentWillUnmount() {
if (this.wrappedComponent) {
this.wrappedComponent.removeEventListener('mouseenter', this.handleMouseEnter)
this.wrappedComponent.removeEventListener('mouseleave', this.handleMouseLeave)
}
}
handleMouseEnter() {
this.setState({componentIsHovered: true})
}
handleMouseLeave() {
this.setState({componentIsHovered: false})
}
bindListeners(wrappedComponentInstance) {
console.log('wrappedComponentInstance', wrappedComponentInstance)
if (!wrappedComponentInstance) {
return
}
this.wrappedComponent = ReactDOM.findDOMNode(wrappedComponentInstance)
this.wrappedComponent.addEventListener('mouseenter', this.handleMouseEnter)
this.wrappedComponent.addEventListener('mouseleave', this.handleMouseLeave)
}
render() {
const props = Object.assign({}, this.props, {ref: this.bindListeners})
return (
<WrappedComponent
componentIsHovered={this.state.componentIsHovered}
{...props}
/>
)
}
}
}
The problem is that this only seems to work when WrappedComponent is a class component — with functional components the ref is always null. I would just as soon place the WrappedComponent inside <div></div> tags in my HOC and carry out the event detection on that div wrapper, but the problem is that even plain div tags will style the WrappedComponent as a block element, which doesn’t work in my use case where the HOC should work on inline elements, too. Any suggestions are appreciated!
You can pass the css selector and the specific styles you need to the Higher Order Component like this:
import React, {Component} from 'react';
const Hoverable = (WrappedComponent, wrapperClass = '', hoveredStyle=
{}, unhoveredStyle={}) => {
class HoverableComponent extends Component {
constructor(props) {
super(props);
this.state = {
hovered: false,
}
}
onMouseEnter = () => {
this.setState({hovered: true});
};
onMouseLeave = () => {
this.setState({hovered: false});
};
render() {
return(
<div
className={wrapperClass}
onMouseEnter= { this.onMouseEnter }
onMouseLeave= { this.onMouseLeave }
>
<WrappedComponent
{...this.props}
hovered={this.state.hovered}
/>
</div>
);
}
}
return HoverableComponent;
};
export default Hoverable;
And use Fragment instead of div to wrap your component:
class SomeComponent extends React.Component {
render() {
return(
<Fragment>
<h1>My content</h1>
</Fragment>
)
}
And then wrap it like this
const HoverableSomeComponent = Hoverable(SomeComponent, 'css-selector');