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...?