1

I have the following higher order component that I am trying to wrap in a container element that is supplied as a prop:

import React, { PropTypes } from 'react';

export default (Component) => {
  return class extends React.Component {
    static propTypes = {
      containerElement: PropTypes.element
    }

    static defaultProps = {
      containerElement: <div />
    };

    componentDidMount() {
      console.log(this.el);
    }

    render() {
      const containerProps = {
        ref: (el) => this.el = el
      };

      return React.cloneElement(containerElement, containerProps, Component);
    };
  }
}

I then wrap a component like this:

export default AnimationComponent(reduxForm({
  form: 'newResultForm',
  validate
})(NewResultForm));

But when I log the element in componentDidMount it is an empty <div/>.

Why is the passed in component not a child of the newly created container element?

1 Answer 1

2

Your method of writing a Higher Order Component is a little unorthodox. React developers typically don't have to write functions that accept components and return a new class definition unless they're writing something like redux-form itself. Perhaps instead of passing Component as an argument, see if passing it in props.children will work for you:

<AnimationComponent>{NewResultForm}</AnimationComponent>

I'd define AnimationComponent like the following:

export default class AnimationComponent extends React.Component {
    static propTypes = {
        containerElement: React.PropTypes.element
    };

    static defaultProps = {
        containerElement: <div />
    };

    render () {
        // For each child of this component,
        // assign each a ref and store it on this component as this[`child${index}`]
        // e.g. this.child1, this.child2, ...
        // Then, wrap each child in the container passed in on props:

        return React.Children.map(this.props.children, (child, index) =>
            React.cloneElement(
                this.props.containerElement,
                {ref: ref => this[`child${index}`] = ref},
                React.cloneElement(child)
            )
        );
    }
}

Instead of wrapping the form component in AnimationComponent, just export the connected form class:

export default reduxForm({
    form: 'newResultForm',
    validate
})(NewResultForm));

Now instead of being stuck with how AnimationComponent was configured in NewResultForm's file, we can configure it to our liking where we end up rendering the form. In addition to providing flexibility, the information needed to configure AnimationComponent will be more pertinent where it gets rendered:

export default class MyApp extends React.Component {
    render() {
        return (
            <AnimationComponent containerComponent="span">
                <NewResultForm />
            </AnimationComponent>
        );
    }
}

I hope this helped!

Sign up to request clarification or add additional context in comments.

6 Comments

containerElement can be a component, e.g. containerElement: <div />
You're right, my mistake. I was assuming the same rules for createElement and createFactory applied also to cloneElement. I'll remove that part from my answer. Thanks for pointing this out.
thanks, also you are just returning React.children.map which is not a react element. i used your approach only it was react.clonelement(containerelement, {ref: ref => this.el = ref}, this.props.children)
Well, children.map will return React.cloneElement(...). for each child, so you're right in that it's not an element but since this arrow function is invoked immediately, what gets returned is the result of each call to this function from the map. This allows you to have more than one child on AnimationComponent without breaking it.
have you tried to run the code? it will not work this way
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.