Andrio
Andrio

Reputation: 2078

Why do I get an error trying to render a component in a variable?

I have a situation where I want to render a default component, UNLESS a different one is passed in an object.

  {map(changes, item => {
    const hasChanged = item.fromValue !== item.toValue;
    const Component = item.Component;

    return (
      <div key={uuid.v4()} className="row">
        <div className="col-md-6">
          {Component ? (
            <Component
              key={uuid.v4()}
              labelColor={hasChanged ? '#AB2912' : ''}
              backgroundColor={hasChanged ? '#FFEE7E' : ''}
              shouldBoldValue={hasChanged}
              label={item.label}
              cssClasses="preview-item-padding"
              capitalize={false} />
          ) : (
              <SimpleLabelText
                key={uuid.v4()}
                labelColor={hasChanged ? '#AB2912' : ''}
                backgroundColor={hasChanged ? '#FFEE7E' : ''}
                shouldBoldValue={hasChanged}
                label={item.label}
                text={item.toValue}
                cssClasses="preview-item-padding"
                capitalize={false} />
            )}

        </div>
      </div>
    );
  })}

In that code above, I want to render SimpleLabelText unless the item object has a Component property. Component is a jsx tag created in a different file.

I don't think it matters, but here is the function that creates the tag in the other file (this is temporary, I'm going to add stuff to it later)

function getCustomComponent(text) {
  return (
    <SimpleLabelText
      text='something'
    />
  );
}

However, when I run this, the whole thing explodes and I get this error message;

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: . Did you accidentally export a JSX literal instead of a component?

How can I properly render the jxg tag that is getting assigned to Component?

Upvotes: 3

Views: 2199

Answers (2)

Panther
Panther

Reputation: 9408

I hope your item.component is the result of calling getCustomComponent(). Here you need to notice that getCustomComponent returns an object and not an function or class. So you have to directly render it like {item.Component} instead of using this -> < /> notation.

Keeping this in mind, your code would transform to something like below.

 return (
  <div key={uuid.v4()} className="row">
    <div className="col-md-6">
      { item.Component ? item.Component : (
          <SimpleLabelText
            key={uuid.v4()}
            labelColor={hasChanged ? '#AB2912' : ''}
            backgroundColor={hasChanged ? '#FFEE7E' : ''}
            shouldBoldValue={hasChanged}
            label={item.label}
            text={item.toValue}
            cssClasses="preview-item-padding"
            capitalize={false} />
        )}

    </div>
  </div>
);

But this does not entirely solve your problem. The props that you need to pass is not getting passed now(in case of item.Component being present). So you might need to look at React.cloneElement. You can read about it here https://reactjs.org/docs/react-api.html#cloneelement and some examples online of how to get your props passed to your item.Component and render the same.

Upvotes: 3

Peter Cheng
Peter Cheng

Reputation: 758

Try this

{ () => (Component ? <Component /> : <SimpleLabelText />) }

abbreviated for brevity. The inside of the brackets needs to be a function that returns your component. The other main issue that causes this error message would be from your import statements, so please include that as well.

Upvotes: 2

Related Questions