0
votes

I'm building a configurable Avatar component that composes different avatar characteristics components depending on the type of avatar we want to put together. E.g. A pirate avatar is composed from a PegLeg component and a Parrot component.

The end goal is to have a const mapping with all characteristics such that:

const characteristicMapping = {
  pegleg: PeglegComponent, // component needed in Pirate avatar
  parrot: ParrotComponent, // component needed in Pirate avatar
  wand: WandComponent, // not used in Pirate, but used in Wizard.
};

And then based on a prop (array of characteristics) we compose the characteristics we needed, e.g.

// For Pirate avatar with Pirate char. set to default value
const Avatar = ({ avatarCharacteristics = ['pegleg', 'parrot'] }) => (
  <div>
    {avatarCharacteristics.map(c => characteristicsMapping[c]).map(
      (AvatarComponent, i) => <AvatarComponent key={i} />)}
  </div>
);

My understanding is, as long as AvatarComponent has its first letter capitalized, this should work, and it does!

However, I seem to have issues depending on how PeglegComponent and ParrotComponent are imported.

I'm using a babel package named Easy Import to handling packages/namespaces.

Current both PeglegComponent/ParrotComponent (sep files) have Easy Import package names such as:

// @package Components/Avatar/Characteristics/PeglegComponent
export default PeglegComponent; 

// @package Components/Avatar/Characteristics/ParrotComponent
export default ParrotComponent;

One folder down from Characteristics, I have an index.js file that's laid out:

// @package Components/Avatar

import PeglegComponent from 'Components/Avatar/Characteristics/PeglegComponent';
import ParrotComponent from 'Components/Avatar/Characteristics/ParrotComponent';

export default {};
export { PeglegComponent, ParrotComponent };

So the imports in the composing Avatar component the imports look like:

import { PeglegComponent, ParrotComponent } from 'Components/Avatar';

const characteristicsMapping = {
  pegleg: PeglegComponent, // component needed for Pirate Avatar
  parrot: ParrotComponent, // component needed for Pirate Avatar
};

// For Pirate avatar with Pirate char. set to default value
const Avatar = ({ avatarCharacteristics = ['pegleg', 'parrot'] }) => (
  <div>
    {avatarCharacteristics.map(c => characteristicsMapping[c]).map(
      (AvatarComponent, i) => <AvatarComponent key={i} />)}
  </div>
);

In this scenario I get the error:

Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports

More importantly if I console.log characteristicsMapping I get {pegleg: undefined, parrot: undefined}

So for whatever reason the import is failing. The weird thing is, this seems to be solely a dynamic component name issue. E.g. If I do this it works, with the exact same import:

import { PeglegComponent, ParrotComponent } from 'Components/Avatar';

// For Pirate avatar with Pirate char. set to default value
const Avatar = () => (
  <div>
    <PeglegComponent />
    <ParrotComponent />
  </div>
);

At first I thought that I simply couldn't dynamic component name, that feature went away, however, then I discovered this would work.

import PeglegComponent from 'Components/Avatar/Characteristics/PeglegComponent';
import ParrotComponent from 'Components/Avatar/Characteristics/ParrotComponent';

const characteristicsMapping = {
  pegleg: PeglegComponent, // component needed for Pirate Avatar
  parrot: ParrotComponent, // component needed for Pirate Avatar
};

// For Pirate avatar with Pirate char. set to default value
const Avatar = ({ avatarCharacteristics = ['pegleg', 'parrot'] }) => (
  <div>
    {avatarCharacteristics.map(c => characteristicsMapping[c]).map(
      (AvatarComponent, i) => <AvatarComponent key={i} />)}
  </div>
);

Also if I have an index.js file in the same folder as PeglegComponent/ParrotComponent and then import from there it works.

In Components/Avatar/Characteristics there's a index.js with:

// @package Components/Avatar/Characteristics

import PeglegComponent from 'Components/Avatar/Characteristics/PeglegComponent';
import ParrotComponent from 'Components/Avatar/Characteristics/ParrotComponent';

export default {};
export { PeglegComponent, ParrotComponent };

Then this works:

import { PeglegComponent, ParrotComponent } from 'Components/Avatar/Characteristics';

const characteristicsMapping = {
  pegleg: PeglegComponent, // component needed for Pirate avatar
  parrot: ParrotComponent, // component needed for Parrot avatar
};

// For Pirate avatar with Pirate char. set to default value
const Avatar = ({ avatarCharacteristics = ['pegleg', 'parrot'] }) => (
  <div>
    {avatarCharacteristics.map(c => characteristicsMapping[c]).map(
      (AvatarComponent, i) => <AvatarComponent key={i} />)}
  </div>
);

If I import stuff from Characteristics's index.js into Avatar's index.js (one directory up), fails again.

So I have a few work arounds, to get over my issue, but it does seem inconsistent that importing from Components/Avatar I can directly do , but if I try to construct an array of Components from an object mapping, that the component comes up undefined.

Wanted to see if anyone could explain to me why.. is this a JSX issue, React issue or an Easy Import issue? Or one with my understanding of how named/default exports work...?

1

1 Answers

0
votes

It seems to be a simple syntax error.You should add quotes on every import like this:

import PeglegComponent from './Components/Avatar/Characteristics/PeglegComponent';

Here is the documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import