Home > Software design >  How to succinctly export a wrapped and named function without using two names
How to succinctly export a wrapped and named function without using two names

Time:10-17

I have a function which transforms other functions:

//library:
let transform = function(OriginalComponent) {
  let WrappedComponent (props) => {
    //some transformation 
    return <OriginalComponent {...props} />
  };
  //I specifically need the original component to have a NON EMPTY name here
    Object.defineProperty(WrappedComponent, "name", { value: OriginalComponent.name });
}

I currently use this in a file like so

export const MyWrappedComponent = transform(function MyComponent(props){
  return <h1>Hello {props.name}!</h1>;
});

With this setup I currently need to use different names for the export and the function.

My question is: Can I somehow export this in one line, using just one name instead of two?

I tried:

export function transform(function MyComponent(props){ 
  return <h1>Hello {props.name}!</h1>;
});

But that is not valid, as the export has no name.

I also thought of

export const MyComponent = transform((props) => {
  return <h1>Hello {props.name}!</h1>;
});

But then transform() receives an unnamed component (and it cannot know the export name I believe?)

This is regarding the standards of a library, so I want to keep the example as clean as possible. Naming a function and then naming the export could get confusing. If I have to name both, I'd prefer to use the same name, but I don't see how.

CodePudding user response:

If you want to use a named export, and you want to pass the function directly into transform, you can't (reasonably¹) get around repeating the name, like this:

export const MyComponent = transform(function MyComponent(props){
    return <h1>Hello {props.name}!</h1>;
});

With this setup I currently need to use different names for the export and the function.

Thankfully, you don't; it's perfectly valid to use the same name there, as above.


For what it's worth, there are a couple of issues with the transform function that I noticed:

  1. You can't directly write to the name property of a function, it's read-only. But you can replace the property (because it's configurable) via Object.defineProperty.

  2. It's not returning the wrapped component.

Here's a version with those fixed:

export let transform = function (OriginalComponent) {
    let WrappedComponent = (props) => {
        //some transformation
        return <OriginalComponent {...props} />;
    };
    // I specifically need the original component to have a NON EMPTY name here
    Object.defineProperty(WrappedComponent, "name", {
        value: OriginalComponent.name,
        writable: false,    // This is the default, but I'm including it
                            // here for emphasis
        configurable: true, // You definitely want to set this to `true`
        enumerable: false,  // (Also the default)
    });
    return WrappedComponent;
};

As an alternative, you could put the unwrapped components in an object:

export const components = {
    MyComponent(props) {
        return <h1>Hello ${props.name}!</h1>;
    },
    // ...
};

...and then post-process them:

for (const [name, component] of Object.entries(components)) {
    components[name] = transform(component);
}

But it means that your export is the components object, not the individual components, so you'd end up with usage like this:

import { components } from "./somewhere";
const { MyComponent } = components;
// ...

...which is less than ideal. (And sadly, you can't directly destructure imports.)

  • Related