Roy Prins
Roy Prins

Reputation: 3080

Problem chaining Higher Order Components <Functions are not valid as a React child.>

I created a sample project to demonstrate a problem I have when trying to use two Higher Order Components (hoc) together.

First in isolation (no errors)

================================

The first hoc withStuff takes an injected argument and a prop and passes the sum to the wrapped component.

// withStuff.js
const withStuff = ({argNumber}) => (BaseComponent) => ({propNumber, ...passThroughProps}) => {
  const sum = argNumber+propNumber
  return <BaseComponent sum={sum} {...passThroughProps} />
}

export default withStuff

The second hoc withExtra takes an injected function and doubles the result, passing double to the wrapped component.

// withExtra.js
const withExtra = (extraFunction) => (BaseComponent) => ({...passThroughProps}) => {
  const double = 2*extraFunction()
  return <BaseComponent double={double} {...passThroughProps} />
}

export default withExtra

This is how a Base component would use for instance withStuff (all working fine so far).

// Base.js
import withStuff from './withStuff'

const Base = ({content, sum}) => <div>{content} -sum:{sum}</div>

export default withStuff({argNumber:2})(Base)

=================================

Now comes the problem: trying to use withExtra inside withStuff:

import withExtra from './withExtra'

const withStuff = ({argNumber}) => (BaseComponent) => ({propNumber, ...passThroughProps}) => {
  const sum = argNumber+propNumber
  // this does not work
  return withExtra(()=>sum)(<BaseComponent sum={sum} {...passThroughProps}/>)
}

export default withStuff

This returns an error:

Warning: Functions are not valid as a React child.

Is it because now withStuff is returning a hoc function instead of a component? That function returns a component itself, so I cannot see the problem. How to solve this?

NOTE CODESANDBOX HERE: https://codesandbox.io/s/github/snirp/hoc-test

Upvotes: 1

Views: 402

Answers (4)

Praneeth Paruchuri
Praneeth Paruchuri

Reputation: 1032

This should solve it:

return withExtra(() => sum)(BaseComponent)();

https://codesandbox.io/s/hoc-test-thxh0

Upvotes: 1

Dennis Vash
Dennis Vash

Reputation: 53934

The problem is that you passing the ReactElement and not the component.

Refer to what is JSX behind the scenes.

Note that if you want to add additional properties to given ReactElement you can use cloneElement.

const withStuff = ({ argNumber }) => BaseComponent => ({
  propNumber,
  ...passThroughProps
}) => {
  const sum = argNumber + propNumber;

  const callback = () => sum;

  // Like so you passing the node which leads to error
  // return withExtra(callback)(<BaseComponent sum={sum} {...passThroughProps}

  // Passing the reference
  return withExtra(callback)(BaseComponent);

  // Passing with additional props
  // return withExtra(callback)(React.cloneElement(BaseComponent, ...));

  // Equivalent
  // const WithExtraProps = withExtra(() => sum)(BaseComponent);
  // return <WithExtraProps sum={sum} {...passThroughProps} />;
};

Upvotes: 2

Mohamed Ramrami
Mohamed Ramrami

Reputation: 12701

Your passing JSX to withExtra not a component, Change withStuff like this :

const withStuff = ({argNumber}) => (BaseComponent) => ({propNumber, ...passThroughProps}) => {
  const sum = argNumber+propNumber

  // this works
  // return <BaseComponent sum={sum} {...passThroughProps} />

  const WithExtraComponent = withExtra(()=>sum)(BaseComponent);
  return <WithExtraComponent sum={sum} {...passThroughProps}/>


}

https://codesandbox.io/s/hoc-test-pm02u

Upvotes: 1

Meir
Meir

Reputation: 14395

withExtra is supposed to get a component, so I think this line

return withExtra(()=>sum)(<BaseComponent sum={sum} {...passThroughProps}/>)

should either be:

return withExtra(()=>sum)(BaseComponent)

or

return withExtra(()=>sum)(() => <BaseComponent sum={sum} {...passThroughProps}/>)

Upvotes: 2

Related Questions